Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using mutators for generic fields #149

Closed
Zizico2 opened this issue Jul 3, 2024 · 1 comment
Closed

Using mutators for generic fields #149

Zizico2 opened this issue Jul 3, 2024 · 1 comment

Comments

@Zizico2
Copy link

Zizico2 commented Jul 3, 2024

Using mutators on a generic field doesn't compile: error[E0401]: can't use generic parameters from outer item.

@idanarye
Copy link
Owner

I've tried this, and it really showed that error:

use std::ops::Add;

use typed_builder::TypedBuilder;

#[derive(TypedBuilder)]
struct Foo<T: Add<T> + Default> {
    #[builder(
        via_mutators,
        mutators(
            fn add(self, amount: T) {
                self.bar += amount;
            }
        )
    )]
    bar: T,
}

Using cargo expand to expand the derive macro and then try to build on it, the problem is from here:

#[allow(dead_code, non_camel_case_types, missing_docs)]
#[automatically_derived]
impl<T: Add<T> + Default> FooBuilder<T, ((T,),)> {
    #[allow(clippy::used_underscore_binding, clippy::no_effect_underscore_binding)]
    fn add(self, amount: T) -> FooBuilder<T, ((T,),)> {
        struct TypedBuilderFieldMutator {
            bar: T,
        }
        impl TypedBuilderFieldMutator {
            fn add(&mut self, amount: T) {
                self.bar += amount;
            }
        }
        let __args = (amount);
        let ((bar,),) = self.fields;
        let mut __mutator = TypedBuilderFieldMutator { bar };
        {
            let (amount) = __args;
            __mutator.add(amount);
        }
        let TypedBuilderFieldMutator { bar } = __mutator;
        FooBuilder {
            fields: ((bar,),),
            phantom: self.phantom,
        }
    }
}

Specifically - TypedBuilderFieldMutator which uses T. Rust does not like that - inline types are only "inline" for visibility purposes (and ergonomics, of course) - they don't actually share the generic parametrization of whatever they are defined in.

I'll have to change it to emit this:

struct TypedBuilderFieldMutator<T: Add<T> + Default> {
    bar: T,
    _phantom: ::core::marker::PhantomData<T>,
}

Note that in this specific case we don't need the phantom data, but since the mutator does not necessarily include all the fields it may not use all the generic parameters, and I really don't want to start diagnosing the generics and see which ones are needed so I'm just going to add them all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants