What does this higher-rank trait bound mean?

Motivation: I have several different structures that can store several different kinds of data. I would like to be generic over structures that hold data using generalized associated traits. It's get tricky when one of the structures actually holds a reference to data, with a lifetime.

This minimal example compiles (playground), but what does it do? In the impl for Bar, am I constraining the output to have a lifetime as least as long as 'a? Is there a better way to express this without attaching extra generic parameters to the Swappable trait?

pub struct Foo<A> {
    _foo: A
}
pub struct Bar<'a, A> {
    _bar: &'a A
}
pub trait Swappable
{
    type Output<B> where for<'b> B: 'b;
}
impl <A> Swappable for Foo<A> {
    type Output<B> = Foo<B> where for<'b> B: 'b;
}
impl <'a, A> Swappable for Bar<'a, A> {
    type Output<B> = Bar<'a, B> where for<'b> B: 'b;
}
pub fn main() {
    let _foo: <Foo<i32> as Swappable>::Output<f64> = Foo::<f64> { _foo: 1. };
    let x = 1.;
    let _bar: <Bar<i32> as Swappable>::Output<f64> = Bar::<f64> { _bar: &x };
}

I’m not completely certain, but I think that it’s equivalent to a B:’static bound:

pub trait Swappable
{
    type Output<B:'static>;
}
impl <A> Swappable for Foo<A> {
    type Output<B:'static> = Foo<B>;
}
impl <'a, A> Swappable for Bar<'a, A> {
    type Output<B:'static> = Bar<'a, B>;
}
1 Like

Yes, there's nothing putting a cap on 'b here:

    type Output<B>
    where
        for<'b> B: 'b;

So for<'b> B: 'b effectively means B: 'static (as that is one lifetime that 'b can take on; the most restrictive one).

1 Like

Thank you for the clarification! From an ergonomics standpoint, is there any way to make the compiler infer the lifetime without making it a generic parameter to the trait like this? When writing generic functions it would be nice to be able to write T: Swappable instead of T: Swappable<'a>.

pub trait Swappable<'a>
{
    type Output<B: 'a>;
}
impl <'a, A: 'a> Swappable<'a> for Foo<A> {
    type Output<B: 'a> = Foo<B>;
}
impl <'a, A: 'a> Swappable<'a> for Bar<'a, A> {
    type Output<B: 'a> = Bar<'a, B>;
}

You could put the lifetime on the GAT, but I'm honestly not sure if this is better or worse.

When the lifetime is on the trait, it's generally better to omit explicit bounds if possible.