idanarye/rust-typed-builder

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

westYY123 opened this issue · 1 comments

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

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.