rust-typed-builder icon indicating copy to clipboard operation
rust-typed-builder copied to clipboard

Type level `field_defaults` that only effect `Option<T>` fields

Open Michael-J-Ward opened this issue 1 year ago • 3 comments

Reason

  1. Option semantics in rust are naturally "optional", and ideally wouldn't have to set them on the builder.
  2. It would also remove the need to strip_default on non-optional fields

Desired behavior

  1. Optional fields optional by default. Note: the standard field_attributes(default) would fail here because Wrapper has no default.
#[derive(PartialEq)]
struct Wrapper(i32);


#[derive(PartialEq, TypedBuilder)]
struct Bar {
    // Mandatory Field
    x: i32,

    // Should be optional
    y: Option<i32>,

    z: Wrapper,
}

fn main() {
    assert!(Bar::builder().x(1).z(Wrapper(3)).build() == Foo { x: 1, y: None, z: Wrapper(3) })
    assert!(Bar::builder().x(1).y(2).z(Wrapper(3)).build() == Foo { x: 1, y: Some(2), z: Wrapper(3) })
}
  1. Option specific builder attributes set on the type don't have to be stripped out (modifying the example from the docs)
use typed_builder::TypedBuilder;

#[derive(TypedBuilder)]
#[builder(field_defaults(default, setter(strip_option)))]
struct Foo {
    // Defaults to None, options-stripping is performed:
    x: Option<i32>,

   // Don't need to set `!strip_option` because it isn't an option field to begin with
   // #[builder(setter(!strip_option))]
    y: i32,

    // Defaults to Some(13), option-stripping is performed:
    #[builder(default = Some(13))]
    z: Option<i32>,

    // Accepts params `(x: f32, y: f32)`
    // Similarly, the `!strip_option` attribute is removed
    #[builder(setter(transform = |x: f32, y: f32| Point { x, y }))]
    w: Point,
}

#[derive(Default)]
struct Point { x: f32, y: f32 }

Michael-J-Ward avatar Sep 14 '22 12:09 Michael-J-Ward

Maybe this should just be the default behavior for Option fields, unless the field has a new attribute - #[builder(explicit_option)]?

idanarye avatar Sep 15 '22 11:09 idanarye

Yes - I think that clarifies the suggestions.

  1. Option<T> becomes default optional.
  2. Option<T> specific setter attributes only get applied to Option<T> fields, removing the need for explicit negation on non-option fields.

#[builder(explicit_option)] might then be useful, but my first reaction is "If I need to explicitly set something to None, then there is probably real application-logic meaning behind that choice that would be better as a named enum variant."

Michael-J-Ward avatar Sep 15 '22 13:09 Michael-J-Ward

This may be, but I still wouldn't want to just block this use case.

idanarye avatar Sep 15 '22 16:09 idanarye