Updating Public Fields of Rust Structures with Private Fields

I have a Foo structure that represents an external serialization format. Foo has dozens of fields, and they are all added all the time. Fortunately, all new fields are guaranteed to have reasonable default values.

Rust has good syntax for creating a structure using default values, and then updating a few selected values:

 Foo { bar: true, ..Default::default() } 

Similarly, we can present the idea “this structure may have more fields in a future version” using a private field of the PhantomData type.

But if we combine these two idioms, we get an error:

 use std::default::Default; mod F { use std::default::Default; use std::marker::PhantomData; pub struct Foo { pub bar: bool, phantom: PhantomData<()>, } impl Default for Foo { fn default() -> Foo { Foo { bar: false, phantom: PhantomData, } } } } fn main() { F::Foo { bar: true, ..Default::default() }; } 

This gives us an error:

 error: field `phantom` of struct `F::Foo` is private [--explain E0451] --> <anon>:23:5 |> 23 |> F::Foo { |> ^ 

Logically, I would say that this should work, because we are only updating public fields, and that would be a useful idiom. An alternative is to support something like:

 Foo::new() .set_bar(true) 

... which will be tedious with dozens of fields.

How can I get around this problem?

+7
struct rust
source share
2 answers

Rename phantom to __phantom , make it public and #[doc(hidden)] .

 use std::default::Default; mod foo { use std::default::Default; use std::marker::PhantomData; pub struct Foo { pub bar: bool, // We make this public but hide it from the docs, making // it private by convention. If you use this, your // program may break even when semver otherwise says it // shouldn't. #[doc(hidden)] pub _phantom: PhantomData<()>, } impl Default for Foo { fn default() -> Foo { Foo { bar: false, _phantom: PhantomData, } } } } fn main() { foo::Foo { bar: true, ..Default::default() }; } 

This is not a very unusual template, a living example: std::io::ErrorKind::__Nonexhaustive .

Of course, users will not have any warnings or anything if they prefer to use the __named field anyway, but __ makes the intention clear. If a warning is required, you can use #[deprecated] .

+4
source share

The default field syntax does not work because you are still creating a new instance (even if you are trying to take some field values ​​from another object).

An alternative is to support something like:

 Foo::new() .set_bar(true) 

... which will be tedious with dozens of fields.

I'm not sure that even with many fields this is:

 Foo::new() .set_bar(true) .set_foo(17) .set_splat("Boing") 

significantly more tiring than:

 Foo { bar: true, foo: 17, splat: "Boing", ..Foo::default() } 

Alternatively, you can highlight public fields in your own type:

 pub struct FooPub { pub bar: bool, // other pub fields } pub struct Foo { pub bar: bool, // other pub fields // alternatively, group them: pub public: FooPub, foo: u64, } impl Foo { pub fn new(init: FooPub) { Foo { bar: init.bar, // other pub fields // alternative: public: init // private fields foo: 17u64, } } } 

You would call it the following:

 Foo::new(FooPub{ bar: true }) 

or add fn FooPub::default() so that you can by default execute some of the fields:

 Foo::new(FooPub{ bar: true, ..FooPub::default()}) 
+6
source share

All Articles