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

Add ability to override default implementation

Open ac566 opened this issue 4 years ago • 2 comments

I just started using this crate today and love it.

The only thing I wish it had is a way to define the setter function for specific fields.

For instance I have a data type in a struct that I need to transform before setting that can't easily be converted using Into, or even From, since they have default implementations and specialization is not in the language yet.

I decoded what I needed to do from this repo, but It would be nice to override this behavior in a nicer way

struct A {
  a: some_type
}

struct AWrapper(A);

#[derive(TypedBuilder)]
struct B {
  z: u64,
  a: A,
  b: usize,
}

// here's what I created to override the behavior

impl BBuilder<((u64,), (), (usize,))> {
  pub fn with_a(
    self,
    a: &AWrapper,
  ) -> BBuilder<((u64,), (A,), (usize,))> {
    let (z, _, b) = self.fields;
    let a = (a.0,);

    BBuilder {
      fields: (z, a, b),
      _phantom: self._phantom,
    }
  }

This is pretty cumbersome, and only works in when part of the Builder has already been created.

I've looked around other issues, and this seems similar to skip, however I would rather it failed compiling because there was no method that replaces it.

ac566 avatar Mar 12 '20 00:03 ac566

Ideally with #23 you should be able to do something like this (I'm not fixated on the syntax yet):

#[typed_builder::fill_generics(
    B,
    generic_param = T,
    constraints(without(a)),
    derivatives(
        O(with(a)),
    ),
)]
impl BBuilder<T> {
    pub fn with_a(self, a: AWrapper) -> O {
        self.a(a.0)
    }
}

idanarye avatar Mar 13 '20 01:03 idanarye

Another option is to support something like this:

#[derive(TypedBuilder)]
#[builder(
    builder_only_generics('a),
    builder_only_fields(
	with_a: &'a AWrapper,
    ),
)]
struct B {
    z: u64,
    #[builder(setter(skip), default = with_a.0)]
    a: A,
    b: usize,
}

#23 is a big and complex feature, and something like this would be quicker and easier to implement - and we'll still want it even after we have #23. The idea is that with_a only exists in the builder type, and a only exists in the actual type. So the user sets with_a using a normal setter, and it remains there for the lifetime of the builder, but does not get copied to B. Instead, a's default uses the value fromwith_a to construct a - which gets skipped and doesn't get its own setter.

idanarye avatar Mar 13 '20 03:03 idanarye