Part of the “Rustober” series.

I found it interesting that Rust doesn’t have initializers. (It doesn’t have classes either and those may be related.) You either set all the fields on a struct directly, create a function which acts like a memberwise initializer and is named new(…) by convention, or use a builder pattern:

use std::thread;

struct Point {
    first: i32,
    second: i32,
};

// Set all fields
let p = Point { first: 1, second: 2 };

// Use new function to create a builder
// followed by builder pattern
let builder = thread::Builder::new()
    .name("foo".into())
    .stack_size(32 * 1024);

Here’s an example of a new function. Rust helps us a bit here by allowing to use parameter names directly in place of matching field names. Library struct fields are often private (like Builder above) and the only way to initialize one is through an associated function (= static method = no &self parameter). Of course, the parameters can be arbitrary and don’t have to match the struct fields — it’s simply a function, not an initializer.

impl Point {
    fn new(first: i32, second: i32, last: i32) -> Point {
        Point {
            first,  // Instead of first: first,
            second, // Instead of second: second,
            // Parameter doesn't match field name
            third: last,
        }
    }
}

let p2 = Point::new(1, 2, 3);

According to the “Constructors” chapter in Rustonomicon:

There is exactly one way to create an instance of a user-defined type: name it, and initialize all its fields at once. That’s it. Every other way you make an instance of a type is just calling a totally vanilla function that does some stuff and eventually bottoms out to The One True Constructor.

… The reasons for this are varied, but it largely boils down to Rust’s philosophy of being explicit.

… In concrete contexts, a type will provide a static new method for any kind of “default” constructor. This has no relation to new in other languages and has no special meaning. It’s just a naming convention.