Difference between named lifetime and for<'a> notation

In the following snippet, the Filter trait has a lifetime argument in order to allow implementations to use the borrowing lifetime in the Output associated type.

The Assert1 and Assert2 trait are examples of where clauses to use the Scan trait with a Filter In the first case, a named lifetime is available and it compiles just fine.
But the Assert2 trait based on for<'a> bounds fails to build for reasons i do not understand.

struct Foo;

struct FooFilter<'a> {
    foo: &'a Foo,
}

trait Filter<'a> {
    type Output;

    fn make_filter(&'a mut self) -> Self::Output;
}

impl<'a> Filter<'a> for Foo {
    type Output = FooFilter<'a>;

    fn make_filter(&'a mut self) -> Self::Output {
        FooFilter { foo: &*self }
    }
}

pub trait Scan<Filter> {}

impl<'a> Scan<FooFilter<'a>> for Foo {}

trait Assert1<'a>
where
    Self: Filter<'a>,
    Self: Scan<<Self as Filter<'a>>::Output>,
{
}
impl<'a> Assert1<'a> for Foo {}

trait Assert2
where
    for<'a> Self: Filter<'a>,
    Self: for<'a> Scan<<Self as Filter<'a>>::Output>,
{
}
impl Assert2 for Foo {}

link to playground

Have you seen Higher-Rank Trait Bounds - The Rustonomicon ?

Thanks. It does not seem to help me in this case, except maybe by telling i should not try to use HRTB ?

I understand that this is universal quantification, but why it does not hold in this case ? Output should be bound to 'static ?

Looks like a bug to me, perhaps related to this old report. CC @nikomatsakis?

2 Likes

I have worked around that same bug in the past by introducing an intermediate helper trait to guide the inference. In your case:

trait ScanHelper<'a, F> {}
impl<'a, T, F> ScanHelper<'a, F> for T
where
    T: Filter<'a, Output = F> + Scan<F>,
{}

trait Assert2: for<'a> Filter<'a>
             + for<'a> ScanHelper<'a, <Self as Filter<'a>>::Output> {}
impl Assert2 for Foo {}
2 Likes

So ... how did you figure out that this works/helps? :slight_smile:

thanks A LOT. This is the workaround i've been looking for for days :slight_smile:

So I had to play around with this, armed with @dtolnay’s workaround, to see if we can skip the helper trait. I managed to do that as so:

struct Foo;

struct FooFilter<'a> {
    foo: &'a Foo,
}

trait Filter<'a> {
    type Output;

    fn make_filter(&'a mut self) -> Self::Output;
}

impl<'a> Filter<'a> for Foo {
    type Output = FooFilter<'a>;

    fn make_filter(&'a mut self) -> Self::Output {
        FooFilter { foo: &*self }
    }
}

trait Scan<Filter> {}

// This formulation of the Scan impl, itself with an HRTB, does the trick
impl<T, F> Scan<F> for T
where
    T: for<'a> Filter<'a, Output = FooFilter<'a>>,
{
}

trait Assert1<'a>
where
    Self: Filter<'a>,
    Self: Scan<<Self as Filter<'a>>::Output>,
{
}
impl<'a> Assert1<'a> for Foo {}

trait Assert2
where
    for<'a> Self: Filter<'a>,
    Self: for<'a> Scan<<Self as Filter<'a>>::Output>,
{
}
impl Assert2 for Foo {}

fn main() {}