Summary
If a generic constructor function appears in a struct expansion/update, it should be used to initialize the remaining variables using type inference.
Motivation
The classic generic in Option may not be Default and you have a lot of fields (e.g. in a generic builder)
#[derive(Default)]
struct Builder<T> {
a: Option<A>,
b: Option<B>,
t: Option<T>,
...
}
This (likely) doesn’t do what you want, because the derived impl requires T: Default
, and instead you need:
impl<T> Default for Builder<T> {
fn default() -> Builder<T> {
a: Option::default(),
b: Option::default(),
t: Option::default(),
...
}
}
This gets unwieldy quickly.
Details
A generic constructor function is any function of the form:
fn foo<T>() -> T {
...
}
That is, a function with (at least) one generic argument, no args, and one return value of the expected type.
This definition is quite relaxed, and allows things like:
struct MyBuilder {
x: Option<Foo>,
y: Option<Bar>,
z: Baz
}
impl MyBuilder {
fn new() -> MyBuilder {
MyBuilder { z: Baz, .. Option::default }
}
}
Where Option::default
takes one generic parameter T
, and outputs an Option<T>
. This compiles, because all fields (except z
) are Option
.
This syntax works because fn
aren’t destructurable, and so the update syntax makes no sense for fn
.
Alternatives
Do nothing.
Unresolved questions
Should it be possible to use multiple automatic initializers, if their bounds don’t overlap?
I’m supposed to write more here but idk what I’m missing.