GATs: lifetime parameters or bounds do not match trait definition

I'm rather puzzled by this error:

trait Foo {
    type T<'a>;
    fn foo<'a>(t: Self::T<'a>) -> Self::T<'a>;
}

impl Foo for () {
    type T<'a> = &'a u8;
    
    // ERROR: lifetime params do not match
    // Expected: OK because Self::T<'a> = &'a u8
    fn foo<'a>(t: &'a u8) -> Self::T<'a> {
        t
    }
}

impl Foo for u8 {
    type T<'a> = &'a u8;
    
    // this works
    fn foo<'a>(t: Self::T<'a>) -> Self::T<'a> {
        t
    }
}
error[E0195]: lifetime parameters or bounds on associated function `foo` do not match the trait declaration
  --> src/lib.rs:11:11
   |
3  |     fn foo<'a>(t: Self::T<'a>) -> Self::T<'a>;
   |           ---- lifetimes in impl do not match this associated function in trait
...
11 |     fn foo<'a>(t: &'a u8) -> Self::T<'a> {
   |           ^^^^ lifetimes do not match associated function in trait

For more information about this error, try `rustc --explain E0195`.

(Playground)

Since Self::T<'a> = &'a u8, I expected to be able to use &'a u8 in place of Self::T<'a> in the signature of the implementation, but it seems like Self::T<'a> is treated as distinct from &'a u8.

This substitution works if there's no return type for the method (playground).

It's this issue. And see also. A syntactically trivial but arcane fix is to do:

    //       vvvv
    fn foo<'a: 'a>(t: &'a u8) -> Self::T<'a> {
        t
    }

As for an explanation, well... the method item as defined in the trait "works" something like this:

impl<'a> Fn(Self::T<'a>) for Self::fn#foo<'a> { ... }

Where the method item is parameterized by the lifetime. But in your initial implementation:

fn foo<'a>(t: &'a u8) -> Self::T<'a>

It attempts to work like:

impl<'a> Fn(<()>::T<'a>) for <()>::fn#foo { ... }

Where the method item type does not have a lifetime parameter, and thus there's one type that implements the Fn traits for all lifetimes. This triggers the error.

The 'a: 'a is an obscure way to make the method item type get parameterized by the lifetime.

Here's some related technical explanation (but it assumes you know what early bound[1] and late bound[2] mean).


  1. method is parameterized by the lifetime ↩︎

  2. method is not parameterized by the lifetime ↩︎

1 Like