idanarye / rust-typed-builder

Compile-time type-checked builder derive
https://crates.io/crates/typed-builder
Apache License 2.0
904 stars 52 forks source link

Builder for a same struct is not a same type if it contains different members #133

Closed westYY123 closed 8 months ago

westYY123 commented 8 months ago
use typed_builder::TypedBuilder;

#[derive(PartialEq, TypedBuilder, Debug)]
struct Foo {
    #[builder(default)]
    x: i32,
}

fn main() {
    let mut foo_a = Foo::builder();
    let my_x = Some(20);
    if let Some(my_x) = my_x {
        foo_a = foo_a.x(my_x);
    }
    let foo_a = foo_a.build();
}

Hi, first, I new a Foo struct builder, then i want to set x to some value if a variant is Some, But compiler tells me they are different types, foo_a is FooBuilder<((),)>, and the latter is FooBuilder<((i32,),)>, I wonder is this will be implement in the future or some other way to complete this

idanarye commented 8 months ago

That's the way it's supposed to work. That's what the "typed" in the crate name stands for. That's how this crate does it's compile-time verification.

Foo::builder().build() throws a compile time error, but Foo::builder().x(20).build() doesn't. This is only possible because they are different types and have different methods implemented on them. Similarly, Foo::builder().x(20).x(30) is a compile time error because using the x setter changes the type so that the x setter cannot be used on it again.

You can get what you want though with mutators:

use typed_builder::TypedBuilder;

#[derive(PartialEq, TypedBuilder, Debug)]
struct Foo {
    #[builder(via_mutators, mutators(
            fn x(&mut self, value: i32) {
                self.x = value;
            }
    ))]
    x: i32,
}

fn main() {
    let mut foo_a = Foo::builder();
    let my_x = Some(20);
    if let Some(my_x) = my_x {
        foo_a = foo_a.x(my_x);
    }
    let foo_a = foo_a.build();
}

This does mean you are giving up on the static checking that this crate provides. Unless you do use them for other fields.