How do you construct an object?

Is there an idiom for writing a constructor?

Should it always be something like

struct Stuff {
   foo: u32,
   bar: String,
}

impl Stuff {
  fn new() -> Stuff {
    let foo = 0u32;
    let bar = "".to_string();
    Stuff {
      foo,
      bar,
    }
  }
}

Or should it be something else? I’m not talking about special cases or anything just about what my default go-to thing should be.

I think it’s more common to see Self in that case:

fn new() -> Self {
    let foo = 0u32;
    let bar = "".to_string();
    Self {
      foo,
      bar,
    }
  }
1 Like

Thanks! Didn’t know you could do that!

Also, if your new doesn’t take any parameters, consider implementing Default.

In this case, you could also derive Default to automatically get an implementation equivalent to the one you provided.

1 Like

You can of course omit the local variables and write:

fn new() -> Stuff {
    Stuff {
        foo: 0,
        bar: "".into()
    }
}

which is more compact. I don’t want to discuss of Stuff over Self. I think both are perfectly valid in this case (you can even do Self { foo: 0, bar: "".into() } :wink:

1 Like

Ah, that’s also cool. I don’t like the fact that I have to construct the value with ::default() now because I am used to constructing things with ::new() but it’s probably the right thing to do in this case.

I would write a new function even if you’re also using default. That might seem redundant at first, but the big difference between them is that default comes from the Default trait, and implementing it means your type is default-constructable in generic contexts. Meanwhile users who are directly creating an instance can reach for the shorter and more familiar new function.

1 Like

Keep in mind that you can delegate the function call in your new function to default, e.g.

#[derive(Debug, Default)]
pub struct OneField {
    a: u32,
}

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

fn main() {
    dbg!(OneField::default());
    dbg!(OneField::new());
}

(Playground)

1 Like

Shouldn’t that be Self::default() instead of Default::default()?

Both is correct and possible. I like Default::default() more, because it is not ambiguous, e.g. if you define a method default on your struct, you would call that instead (Playground).

#[derive(Debug, Default)]
pub struct OneField {
    a: u32,
}

impl OneField {
    pub fn new() -> Self {
        Default::default()
    }
    fn default() {
        println!("WRONG");
    }
}

fn main() {
    dbg!(OneField::default());
    dbg!(OneField::new());
}

which will print

WRONG
[src/main.rs:16] OneField::default() = ()
[src/main.rs:17] OneField::new() = OneField {
    a: 0,
}

So, it’s sometimes complicated :wink:

1 Like