Lifetime bound of a Trait that is a member of a struct

Hello Dear Community,

I have a trait type that uses a lifetime parameter (see Explicit lieftime name in type parameter of trait - #7 by rudolfvonkrugstein):

pub trait Values<'a> {
    type Item: 'a;

    fn get(&self, index: usize) -> Self::Item;
}

Now I want to write a "container" type any Type that implements this trait:

Here is my container:

struct Container<V>
    where V: for<'a> Values<'a>,
{
    values: V,
}

I am already unsure, should I not tell rust somehow that A may not live longer than the Container itself?

OK, moving on, I now try to create the container using a concrete implementation for Values:

struct VecValues<A> {
    pub values: Vec<A>
}

impl<'a, A> Values<'a> for VecValues<A>
    where A: 'a
{
    type Item = &'a A;

    fn get(&self, index: usize) -> Self::Item {
        todo!()
    }
}

and finally I try to construct a container (which fails):

fn new_container<A>(v: Vec<A>) -> Container<VecValues<A>>
{
    todo!()
}

The error:

error[E0311]: the parameter type `A` may not live long enough
  --> src/main.rs:27:35
   |
27 | fn new_container<A>(v: Vec<A>) -> Container<VecValues<A>>
   |                                   ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
   |
note: ...that is required by this bound
  --> src/main.rs:22:14
   |
22 |     where V: for<'a> Values<'a>
   |              ^^^^^^^^^^^^^^^^^^
help: consider adding an explicit lifetime bound...
   |
27 | fn new_container<A: 'a>(v: Vec<A>) -> Container<VecValues<A>>
   |                   ++++

I think I understand. I must bound the lifetime. But how do I do that?

Thanks!

This line would mean that V: Values<'static>, since 'static is a valid value for 'a. This is certainly not what you want your trait to do, or what you can implement for a container.

This desugars to

pub trait Values<'a> {
    type Item: 'a;

    fn get<'b>(&'b self, index: usize) -> Self::Item;
}

Now you have a visible problem: the lifetime of the returned Self::Item is unrelated to the lifetime of the borrow on &'b self, so you can't return a borrowed value.

What you really want is to return Self::Item, which has the same lifetime as the borrow itself. Since the borrowed lifetime exists only in the fn get() itself, this means that your Self::Item must actually be generic in the lifetime parameter, so that you could write

fn get<'b>(&'b self, index: usize) -> Self::Item<'b>;

In the past that's the point where people would tell you "but you can't have generic associated items on stable Rust", but you're in luck, because generic associated types were recently stabilized and will land in Rust 1.65.

With GATs, you can do what you want quite simply:

pub trait Values {
    type Item<'a> where Self: 'a;

    fn get(&self, index: usize) -> Self::Item<'_>;
}

struct VecValues<A> {
    pub values: Vec<A>
}

impl<A> Values for VecValues<A> {
    type Item<'a> = &'a A where Self: 'a;

    fn get(&self, index: usize) -> Self::Item<'_> {
        &self.values[index]
    }
}

Just make sure you know of the restrictions of the current GAT implementation.

5 Likes

There is a way to emulate lifetime GATs on stable if you don't want to wait / have a minimum version of Rust to support.

2 Likes

Hi, thanks for the helpful answers!

I now implemented @quinedot approach because I don't want to use an unstable version of rust, and it works well for my usecase.