Why does this lifetime need to be constrained?

I'm trying to implement tokio's Encoder and running into trouble. Why is this not allowed?

error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
 --> src/main.rs:7:6
  |
7 | impl<'a> Bar for Baz {
  |      ^^ unconstrained lifetime parameter
fn main() {
    println!("{}", A { foo: "foo" }.debugify())
}

struct Baz;

impl<'a> Bar for Baz {
    type Item = A<'a>;
    
    fn debugify(&mut self, item: Self::Item) -> String {
        format!("{:?}", item)
    }
}

#[derive(Debug)]
struct A<'a> {
    foo: &'a str,
}

trait Bar {
    type Item: std::fmt::Debug;
    fn debugify(&mut self, item: Self::Item) -> String;
}

(Playground.)

I've tried with type Item = Foo<'_> instead, and that doesn't work either. It seems to me the single method (and thus, the entire trait impl) does not actually need a lifetime constraint. Why is the compiler insisting on having one?

Because it doesn't know where the 'a is coming from. It seems like you want item: Self::Item<'_> as the debugify parameter, because only at the callsite of this function is the lifetime of the item known. That however requires Generic Associated Types, so this is not something you can do yet.

What you can do is implement the trait for &'a mut Baz instead, which is a common pattern in std for avoiding this problem.

Playground

Let's suppose the above worked, what would type X = <Baz as Bar>::Item; be ?

  • <Baz as Bar>::Item must unambiguously refer to one type (this is not a generic type!),

  • and yet in your case you are saying that both A<'static> and A<'not_static> are this one type.

This is because you have used an associated type instead of a generic parameter:

  • generic type parameter:

    trait Bar<Item> {
        fn debugify (self: &'_ mut Self, item: Item)
          -> String
        ;
    }
    
    impl<'a> Bar<A<'a>> for Baz {
        fn debugify (self: &'_ mut Self, item: A<'a>)
          -> String
        { ... }
    }
    
  • generic lifetime parameter (if you still wish to have an associated type):

    trait Bar<'a> {
        type Item : 'a;
    
        fn debugify (self: &'_ mut Self, item: Self::Item)
          -> String
        ;
    }
    
    impl<'a> Bar<'a> for Baz {
        type Item = A<'a>;
    
        fn debugify (self: &'_ mut Self, item: A<'a>)
          -> String
        { ... }
    }
    
4 Likes