40

To create a default struct, I used to see fn new() -> Self in Rust, but today, I discovered Default. So there are two ways to create a default struct:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn new() -> Self {
        Point {
            x: 0,
            y: 0,
        }
    }
}

impl Default for Point {
    fn default() -> Self {
        Point {
            x: 0,
            y: 0,
        }
    }
}

fn main() {
    let _p1 = Point::new();
    let _p2: Point = Default::default();
}

What is the better / the most idiomatic way to do so?

0

2 Answers 2

48

If you had to pick one, implementing the Default trait is the better choice to allow your type to be used generically in more places while the new method is probably what a human trying to use your code directly would look for.

However, your question is a false dichotomy: you can do both, and I encourage you to do so! Of course, repeating yourself is silly, so I'd call one from the other (it doesn't really matter which way):

impl Point {
    fn new() -> Self {
        Default::default()
    }
}

Clippy even has a lint for this exact case!

I use Default::default() in structs that have member data structures where I might change out the implementation. For example, I might be currently using a HashMap but want to switch to a BTreeMap. Using Default::default gives me one less place to change.


In this particular case, you can even derive Default, making it very succinct:

#[derive(Default)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn new() -> Self {
        Default::default()
    }
}

fn main() {
    let _p1 = Point::new();
    let _p2: Point = Default::default();
}
Sign up to request clarification or add additional context in comments.

3 Comments

I'd just like to point out that there's a pretty common pattern for "configuration" structs that looks like let config = MyConfig { option1: 2, option2: None, .. Default::default() }
@WesleyWiser yep! although you aren't restricted to Default::default() there; it can be MyStruct::new() or even an existing variable.
Unfortunately, { ..Default::default() } doesn't work on #[non_exhaustive] structs
1

I agree with the existing answer regarding implementing both, but will add a few extra details for posterity. You can implement Default, then add new automatically with a trait template:

pub(crate) trait New: Sized + Default {
    fn new() -> Self { 
        Default::default() 
    }
} impl<T: Default> New for T { }

Typically I will use this for simple structs, but for structs with significant initialization overhead Default::default will return an empty object (eg. for filling an uninitialized array) whereas new will return a fully initialized instance.

2 Comments

You might want to format your code a bit next time, so that it is clear to the reader instead of a huge one liner
I tend to prefer concision for simple traits like this, particularly when I'd use it across multiple projects. However, in the context of a support site like this I can see the value in spreading it out a bit; edited

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.