How to reduce boilerplate for `fn new()`?

Hello! I have a few usize in my code that are used to index various things.

type Node = usize;
type Waypoint = usize;

I would like to encode the kind of properties they have by wrapping them in a new type.

#[derive(Debug)]
struct Node(pub usize);
#[derive(Debug)]
struct Waypoint(pub usize);

It's better, but this means that the id can be changed at any time anywhere in the code.

#[derive(Debug)]
struct Node(usize);
impl Node {
    pub fn new(id: usize) -> Node {
        Node(id)
    }
}
#[derive(Debug)]
struct Waypoint(usize);
impl Waypoint {
    pub fn new(id: usize) -> Waypoint {
        Waypoint(id)
    }
}

We are getting somewhere! But this feels really verbose. Is there some kind of derive macro in std or some crate that would generate the fn new(...)? Something like this:

#[derive(New, Debug)]
struct Node(usize);
#[derive(New, Debug)]
struct Waypoint(usize);

EDIT: typo in code

Your example is really verbose.

Not only that it does not compile for various reasons.

But this does:

    #[derive(Debug)]
    struct Node {id: usize};

    impl Node {
        pub fn new(id: usize) -> Node {
            Node { id }
        }
    }

    let n1 = Node::new(1);
    let n2 = Node {id: 2};

    println!("{:?}", n1);
    println!("{:?}", n2);

So my question then is: For such a trivially simple struct why bother with implementing "new" for it at all? Just create them in place like my n2 above.

That's awesome, I just didn't know that Node {id: 2}; was a valid syntax.

Does this means that the identifiers on non-pub types are public, and thus cannot be renamed without potentially breaking downstream users?

Hi,
well the thing is, that your second solution only works if you have the definition of the struct and the "instantiation" in the same module. If this is not the case the compiler will complain at your second example, that id is a private field, hence you'd need a new constructor for this:

mod Foo {
    #[derive(Debug)]
    pub struct Node {id: usize}
    
    impl Node {
        pub fn new(id: usize) -> Node {
            Node { id }
        }
    }
}


fn main() {
    let n1 = Foo::Node::new(1);
    let n2 = Foo::Node {id: 2}; // <-- compiler complains here!

    println!("{:?}", n1);
    println!("{:?}", n2);
}
error[E0451]: field `id` of struct `Foo::Node` is private
  --> src/main.rs:16:25
   |
16 |     let n2 = Foo::Node {id: 2};
   |                         ^^^^^ private field

This is indeed a good thing (otherwise you couldn't enforce invariant in the constructor if users could "just" create the object without using the constructors.

This means that my initial question is still valid. Is there a way to auto-generate the new() function?

You can use something like derive-new to generate a new() static method. The derive-more crate also has #[derive]'s for converting to/from other types when the conversion is trivial, as well as its own #[derive] for generating a constructor.

I'd be tempted to ask whether you actually need this in the first place, though...

If you're generating a function which takes as many arguments as your struct has fields and will just populate those fields, why not drop the constructor, make the fields public, and let callers create the object themselves? Auto-generated constructors don't actually help, because the moment you add a new private field the constructor will get a new argument and you'll break all downstream users.

Have a look at the Defining and Instantiating Structs chapter from The Rust Programming Language.

To create an instance of some type, you just need to write the object literal (e.g. Node(42) for your newtype struct, or Node { id: 42 } for a struct with named fields). Unlike languages with inheritance (C++, Python, Java, etc.) the constructor isn't special, it's just a name we use for some function that creates a new instance of something.

This is incorrect. Non-public fields are only accessible within the module they are defined in. It sounds like you might need to check out the Defining Modules to Control Scope and Privacy chapter from The Rust Programming Language.

1 Like

Thanks, it's exactly what I needed.

I am manipulating a graph, and I need to make the difference between crossroad (that are connected to 3 or more points), dead-end (that are connected to 0 or 1 point), and waypoints (that are connected to exactly 2 other points, those points draws the curve of a road). I want to enforce those properties at compile time.

Because I don't want the fields to be writable once created (they can be copied, but not modified).

I was just highly confused when I read ZiCog's answer. I was obviously already using this syntax in my constructors, but the way he phrased it made me thing that it was also possible to use it anywhere (A bit like Foo{1, 2} in C++). And my stupid brain thought that it was something new that I never saw.

That's because you can use it anywhere. Node { id: 2 } is a valid expression which evaluates to an instance of Node with the id field set to 2.

Another option is to make a macro_rules macro which will define your type and add any necessary methods.

macro_rules! strongly_typed_index {
    ($name:ident) => {
        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
        pub struct $name(usize);

        impl $name {
            pub fn new(index: usize) -> Self { $name(index) }

            pub fn value(self) -> usize { self.0 }
        }
    };
}

strongly_typed_index!(CrossRoads);
strongly_typed_index!(WayPoint);

fn main() {
    let crossroads = CrossRoads::new(1);
    let waypoint = WayPoint::new(1);

    let you_cant_mix_crossroads_and_waypoints: WayPoint = crossroads;
}

(playground)

I want to mention that this is only half true. Non-public fields are only accessible within the module they are defined in and in the module's childs.

Which means we can separate some implementation blocks into child modules, e.g.:

mod a {
    #[derive(Debug)]
    pub struct A { id: usize }
    
    mod b {
        use super::A;
        
        impl A {
            pub fn new() -> Self {
                A { id: 42 }
            }
        }
    }
}

fn main() {
    let a = a::A::new();
    
    println!("This is a: {:?}", a);
    
    // Fails because `id` is private
    // println!("This is a.id: {:?}", a.id);
}

Here is the playground link.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.