patterns/idioms/default.md

2.2 KiB

The Default Trait

Description

Many types in Rust have a constructor. However, this is specific to the type; Rust cannot abstract over "everything that has a new() method". To allow this, the Default trait was conceived, which can be used with containers and other generic types (e.g. see Option::unwrap_or_default()). Notably, some containers already implement it where applicable.

Not only do one-element containers like Cow, Box or Arc implement Default for contained Default types, one can automatically #[derive(Default)] for structs whose fields all implement it, so the more types implement Default, the more useful it becomes.

On the other hand, constructors can take multiple arguments, while the default() method does not. There can even be multiple constructors with different names, but there can only be one Default implementation per type.

Example

use std::{path::PathBuf, time::Duration};

// note that we can simply auto-derive Default here.
#[derive(Default, Debug, PartialEq)]
struct MyConfiguration {
    // Option defaults to None
    output: Option<PathBuf>,
    // Vecs default to empty vector
    search_path: Vec<PathBuf>,
    // Duration defaults to zero time
    timeout: Duration,
    // bool defaults to false
    check: bool,
}

impl MyConfiguration {
    // add setters here
}

fn main() {
    // construct a new instance with default values
    let mut conf = MyConfiguration::default();
    // do something with conf here
    conf.check = true;
    println!("conf = {:#?}", conf);
        
    // partial initialization with default values, creates the same instance
    let conf1 = MyConfiguration {
        check: true,
        ..Default::default()
    };
    assert_eq!(conf, conf1);
}

See also