Using .. to instantiate structs with private and public fields


#1

This is something that confuses me - I thought that

let x = Hello {
    name: "Link",
    ..y
}

was pretty much equivalent to

let mut x = y;
x.name = "Link";
let x = x;

. But then why can’t I use the .. syntax with structs that have both private and public fields, changing only the public fields? This might be useful if I was going to, for example, build a builder-like struct, using a private field to keep the struct extensible (kind of like how people use a hidden __Nonexhaustive variant to keep enums extensible).


#2

If part of a structure is private then outside the module you must use a public function to construct since your code does not know the private detail.

Your second code snippet isn’t constructing a new instance only modifying. (And moving to a different variable identifier.)


#3

Interestingly, it’s called struct update syntax in the book, which implies that it’s not the same as just constructing a new instance. Anyway, isn’t the fomer snippet just sugar for the latter?


#4

It’s closer to a shorthand for

let x = Hello {
    name: "Link",
    something: y.something,
    something_else: y.something_else,
}

with the compiler just going through and listing out all the fields you haven’t explicitly given a value to. It’s only really an “update” syntax if you’re thinking about immutable data structures, where to update part of a value you just construct a new instance with that part changed.


#5

Builders are typically implemented with methods exposing the configurable parts, and then you can hide fields and/or other private state the normal way. You don’t generally need struct update syntax there because the builder methods move the self value in and out:

pub struct Builder { ... }
impl Builder {
    pub fn new(/* mandatory config */) -> Self { ... }

    pub fn with_some_bool(self, val: bool) -> Self {
        self.bool_val = val;
        self
    }
  
   pub fn with_some_i32(self, val: i32) -> Self {
      self.i32_val = val;
      self
   }

   pub fn build(self) -> ThingBeingBuilt { ... }
}

let thing = Builder::new().with_some_bool(true).with_some_i32(5).build();

#6

I know, but that method is really boilerplate-y, so I was looking for a nicer one.


#7

I’m pretty sure (but not positive :slight_smile:) I’ve seen some crates that derive a builder for you - try searching on crates.io.