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

Add helper macros for matching on and transforming the generic parameter of the builder type

Open idanarye opened this issue 4 years ago • 5 comments

In #22 we added traits for extending the builder type, but using several of them together (a common usecase for extending the builder type) can become very verbose:

#[derive(TypedStruct)]
pub struct Foo {
    pub bar: u32,
    pub baz: u32,
}

impl<F> FooBuilder
where F: FooBuilderWithoutBar,
    <F as FooBuilderWithoutBar>::With: FooBuilderWithoutBaz
{
    pub fn bar_and_baz(bar: u32, baz: u32) -> FooBuilder<
        <<F as FooBuilderWithoutBar>::With as FooBuilderWithoutBaz>::With
    > {
        self.bar(bar).baz(baz)
    }
}

To make this more doable, we want helper macros:

impl<F> FooBuilder
where typed_builder::can_set!(F: Foo; bar, baz)
{
    pub fn bar_and_baz(bar: u32, baz: u32) -> FooBuilder<typed_builder::set_fields!(F: Foo; bar, baz)> {
        self.bar(bar).baz(baz)
    }
}

idanarye avatar Dec 12 '19 14:12 idanarye

Problem - it doesn't look like we can use macros in trait bounds position yet...

idanarye avatar Dec 12 '19 14:12 idanarye

Problem - it doesn't look like we can use macros in trait bounds position yet...

proc-macros might be a good fit? I am thinking something along the lines of:

#[typed_builder::can_set(F = Foo, bar, baz)]
impl FooBuilder {
    #[typed_builder::set_fields(F = Foo, bar, baz)]
    pub fn bar_and_baz(bar: u32, baz: u32) -> FooBuilder {
        self.bar(bar).baz(baz)
    }
}

While I think this solution looks better, I don't like that it seems more "magical"/less explicit...

mwilliammyers avatar Dec 12 '19 18:12 mwilliammyers

Proc macros can do anything - the problem is that you need to deal with all the edge cases yourself, and you don't know what all the edge cases are. So - they are dangerous.

I'm willing to do it with a proc macro if I have to, though I want to alert readers regarding where the magic happens, maybe by prefixing the replaced-by-magic parts with @ or something similar...

I opened a Reddit thread to see if someone there can come up with a better solution: https://www.reddit.com/r/rust/comments/e9q45i/how_can_i_use_macros_to_help_gluing_complex_trait/

idanarye avatar Dec 12 '19 18:12 idanarye

With Rust 1.40.0 we can have macros emit macro_rules!. I wonder if I can use that somehow to make #22 obsolete? Make #[derive(TypedBuidler)] generate some that will be used by #[typed_builder::***]?

idanarye avatar Dec 19 '19 23:12 idanarye

With Rust 1.40.0 we can have macros emit macro_rules!. I wonder if I can use that somehow to make #22 obsolete? Make #[derive(TypedBuidler)] generate some that will be used by #[typed_builder::***]?

This seems possible: https://github.com/idanarye/rust-poc-macro-emit-data

This is a better solution than the trait-based one, because with traits we need to define the exact path of transformation, making the API more cumbersome to use.

This change may seem to make #21 redundant - but it doesn't really, because it's still helpful to have a single generic parameter (e.g. if you want to pass the builder to some other function that doesn't internally care about it's state)

idanarye avatar Dec 20 '19 15:12 idanarye