Problem about writing macro to auto impl trait for all possible types

I want to write a macro to automatically generate implementation of a trait for all types that can implement it. But I encountered a problem.

Usage

#[impl_trait_alias]
pub trait FutureIter<Err>
where
    Self: Future<Output = Result<Self::FutOut, Err>>,
    Self::FutOut: Iterator<Item: Display>,
{
    type FutOut;
}

it generates

// ... original trait item

impl<
    Err,
    #[allow(non_camel_case_types)] __ImplTraitAlias_Self,
    #[allow(non_camel_case_types)] __ImplTraitAlias_FutOut,
> FutureIter<Err> for __ImplTraitAlias_Self
where
    Self: Future<Output = Result<__ImplTraitAlias_FutOut, Err>>,
    __ImplTraitAlias_FutOut: Iterator<Item: Display>,
{
    type FutOut = __ImplTraitAlias_FutOut;
}

And you can express a bundle of trait bounds with only typing a name!

fn get() -> impl FutureIter<String> {
    async { Ok([123].into_iter()) }
}

Problem

As you can see, the existing macro logic contains an operation that replacing all Self::FutOut to a custom generic __ImplTraitAlias_FutOut. Of course the generated code in this example works fine, but the shortcoming is obvious: The user can only write Self::FutOut and any other style is unacceptable, like <Self>::FutOut, <Self as FutureIter<Err>>::FutOut, macros, etc.

So the problem is: is there a way to realize the auto-implementation without modifying where-predicates? Thanks.


Note: Directly keep the origin where-predicates unchanged will trigger an compile error.

The code:

impl<
    Err,
    #[allow(non_camel_case_types)] __ImplTraitAlias_Self,
    #[allow(non_camel_case_types)] __ImplTraitAlias_FutOut,
> FutureIter<Err> for __ImplTraitAlias_Self
where
    Self: Future<Output = Result<Self::FutOut, Err>>,
    Self::FutOut: Iterator<Item: Display>,
{
    type FutOut = __ImplTraitAlias_FutOut;
}

The error:

error[E0275]: overflow evaluating the requirement `<__ImplTraitAlias_Self as FutureIter<Err>>::FutOut == _`

The implementor will only be Self for users writing their own futures, which is rare.

But anyway, can you provide an example where Self::FutOut works but <Self>::FutOut does not? I wasn't able to reproduce.

Ah, that's not what I meant. I mean In my existing macro generation logic, I simply replace all Self::FutOut with __ImplTraitAlias_FutOut, makes only Self::FutOut can be recognized and being replaced correctly, other write styles like <Self>::FutOut cannot be recognized and replaced.
We have to replace Self::FutOut(or <Self>::FutOut), leaving them unchanged in the generated impl code will cause compile time errors.

1 Like

Oh! Sorry, it flew right over my head that this was a problem with the macro itself.

(Unfortunately I won't be much help.)

Even you can't help? That seems really hard. :person_facepalming:

1 Like

I appreciate the sentiment, but macros aren't my forte at all -- hopefully someone else can help :slight_smile:.

To be honest, I don't think what you're trying to do is possible because Rust normally only allows trait implementation for types within the same crate. Could you please show me the macro implementation you're using?

This repo is my current implementation. I haven't had time to write README, I use AI generated one. I'll rewrite it later, sorry just bear with it for now :downcast_face_with_sweat:

macros aren't my thing, but if it's useful to you, I recall a similar pattern in Axum where they use macros to impl traits for many types, e.g. impl_into_response and impl_handler

I'm not making a macro to generate impl code for a certain trait but to generate impl code for any trait if that trait satisfy the requirements. Thanks for reply as well.

one thing that confused me in your example is your "unconventional" way to write the bound on the associated type.

typically, if the associated type is part of the trait being declared, I would put the required trait bounds on the associated types, instead of a where clause on the trait.

also, I would prefer supertrait over a trait bound on Self.

so, I would probably write your original example like this:

#[impl_trait_alias]
pub trait FutureIter<Err>: Future<Output = Result<Self::FutOut, Err>> {
    type FutOut: Iterator<Item: Display>;
}

and this is much simpler for the macro to parse, and a blanket implementation like this can be easily generated:

impl<Err, T, FutOut> FutureIter<Err> for T
where
    T: Future<Output = Result<FutOut, Err>>,
    FutOut: Iterator<Item: Display>,
{
    type FutOut = FutOut;
}

But you also modified Self::FutOut at T bounds, which is the key point of the problem.

impl<Err, T, FutOut> FutureIter<Err> for T
where
    T: Future<Output = Result</*Self::*/FutOut, Err>>,
    FutOut: Iterator<Item: Display>,
{
    type FutOut = FutOut;
}