`self` does not live long enough - strange lifetime issue

I have been pulling my hair on this one for quite some time now ; and it made me realize that no, I still don't grok lifetimes. :frowning: The following code is a simplified version of my actual problem, but the errors I am getting are the same.

Let's say I have a trait for anything that can provide a buffer:

trait BufferProvider<'a> {
    type BufferType: 'a;
    
    fn get_buffer(&'a self) -> Self::BufferType;
}

Buffers returned by implementors of this trait shall not outlive the object that provided them, which I think is correctly expressed by the 'a lifetime: anything returned by get_buffer() cannot outlive self.

Let's implement this trait:

struct Pool {
}

struct Buffer<'a> {
    _pool: &'a Pool,
}

impl<'a> BufferProvider<'a> for Pool {
    type BufferType = Buffer<'a>;
    
    fn get_buffer(&'a self) -> Buffer<'a> {
        Buffer { _pool: self }
    }
}

Buffers hold a (dummy for this example) reference to the Pool that provided them and thus shall not outlive it ; the trait looks easy to implement in this case.

Now I want a struct that will make use of a BufferProvider from a few methods:

struct BufferUser<'a, BP>
where BP: BufferProvider<'a>,
{
    provider: BP,
    _l: PhantomData<&'a BP>,
}

Since 'a is required to mention BufferProvider, I need to use a PhantonData otherwise that declaration won't compile.

And now the real fun:

impl<'a, BP> BufferUser<'a, BP>
where BP: BufferProvider<'a>,
{
    fn do_something(&'a self) {
        let buffer = self.provider.get_buffer();
        // do something with the buffer...
        drop(buffer);
    }
    
    fn run(self) {
        self.do_something();
    }
}

First let's look at do_something(). It gets a buffer from the provider, and drops it after a while. There is no way the buffer outlives self or self.provider. Yet, unless I specify the lifetime 'a for self, I get the following error:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:36:36
   |
36 |         let buffer = self.provider.get_buffer();
   |                                    ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 35:5...
  --> src/main.rs:35:5
   |
35 |     fn do_something(&self) {
   |     ^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:36:22
   |
36 |         let buffer = self.provider.get_buffer();
   |                      ^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 31:6...
  --> src/main.rs:31:6
   |
31 | impl<'a, BP> BufferUser<'a, BP>
   |      ^^
note: ...so that the types are compatible
  --> src/main.rs:36:36
   |
36 |         let buffer = self.provider.get_buffer();
   |                                    ^^^^^^^^^^
   = note: expected `&'a BP`
              found `&BP`

In a way that makes sense ; we need to restrict 'a to self, and after that this method compiles.

But then, run() is unhappy:

error[E0597]: `self` does not live long enough
  --> src/main.rs:42:9
   |
31 | impl<'a, BP> BufferUser<'a, BP>
   |      -- lifetime `'a` defined here
...
42 |         self.do_something();
   |         ^^^^---------------
   |         |
   |         borrowed value does not live long enough
   |         argument requires that `self` is borrowed for `'a`
43 |     }
   |     - `self` dropped here while still borrowed

This one confuses me beyond words. How can do_something(), which does not return anything, borrow self for longer than the duration of run()? My understanding of the 'a lifetime is that 'a represents the lifetime of self within do_something(), since that's the only place where it is used in the impl block. But from this message it seems like I am in fact completely missing the mark.

Could someone explain me what is going on in this code? Also, how can I make it work? I have posted a playground populated with the code in this example for those kind enough to try and solve this. :slight_smile:

Your struct does not contain any references, so it should not be annotated with a lifetime. Instead you should use the for<'a> syntax to specify your where bound. Additionally, it is unnecessary to have the where bound on the struct — its enough to just have it on the impl block.

struct BufferUser<BP> {
    provider: BP,
}

impl<BP> BufferUser<BP>
where
    BP: for<'a> BufferProvider<'a>,
{
    fn do_something(&self) {
        let buffer = self.provider.get_buffer();
        // do something with the buffer...
        drop(buffer);
    }
    
    fn run(self) {
        self.do_something();
    }
}

Yes, in this case the lifetime 'a is annotated on the struct, and lifetimes annotated on a struct always contain the entire struct itself. This means that borrowing something for the lifetime 'a borrows it for a duration that contains the entire lifetime of the struct.

The reason it had to borrow it for this duration is that you only implemented BufferProvider<'a> for one specific lifetime called 'a, and so it could only be used with that lifetime. The for<'a> syntax means "for all lifetimes 'a", and this allows you to use the trait with any given lifetime, including e.g. the a lifetime inside the method of do_something.

7 Likes

If it helps here's a working playground showcasing Alice's solution. The for<'a> syntax is called higher-rank trait bounds, and it is documented in the nomicon.

By the way, you don't need type BufferType: 'a; it works fine without it (just type BufferType;).

3 Likes

@alice thanks a lot for this detailed answer! I think I understand what I got wrong, and I did not know about the higher-ranked trait bounds (even though they are mentioned in the reference :sweat:).

I mentioned that my example was a simplified version of my actual problem, and when trying to apply the solution to the actual code I ran into another issue that seems very related.

Truth is, Buffers also have some traits. Here is, say, a trait for Buffers that can be cleared, and its implementation for the Buffer struct:

trait Clearable {
    fn clear(&self);
}

impl<'a> Clearable for Buffer<'a> {
    fn clear(&self) {}
}

I want do_something() to clear the buffer it obtained, so in order to make this work I need to further restrict the impl block of BufferUser to BufferProviders which BufferType also implements Clearable. Something like:

impl<BP> BufferUser<BP> where
    for <'a> BP: BufferProvider<'a>,
    for <'a> <BP as BufferProvider<'a>>::BufferType: Clearable,
{
    fn do_something(&self) {
        let buffer = self.provider.get_buffer();
        buffer.clear();
    }
    ...

To my surprise, this results in another puzzling error when I try to call run() on an instance of BufferUser<Pool>:

error[E0599]: no method named `run` found for struct `BufferUser<Pool>` in the current scope
  --> src/main.rs:55:17
   |
30 | struct BufferUser<BP>
   | --------------------- method `run` not found for this
...
55 |     buffer_user.run();
   |                 ^^^ method not found in `BufferUser<Pool>`
   |
   = note: the method `run` exists but the following trait bounds were not satisfied:
           `<Pool as BufferProvider<'a>>::BufferType: Clearable`

But:

  • Pool implements BufferProvider, so <Pool as BufferProvider> is valid. Besides, the first where clause enforced that, and I got no complain about it.
  • <Pool as BufferProvider>::BufferType is Buffer, which explicitly implements Clearable.

Here I suspect that what did not match was in fact the lifetime <'a>, probably because I am misusing the for clause?

I have also tried to remove for<'a> and use the anonymous lifetime <'_> instead, but to no avail.

Here is another playground showcasing the issue. Once again, I would be very grateful for an explanation for this behavior. :bowing_man:

By the way, you don't need type BufferType: 'a ; it works fine without it (just type BufferType; ).

This works indeed. But in this case, wouldn't it prevent my (arbitrary) invariant that return values of get_buffer() must not outlive self from being enforced? Since BufferType is not a reference, I don't see where else the compiler could get that hint.

Or maybe in that case the invariant will be enforced only on implementations that require it, i.e. these that have an explicit lifetime specified for their BufferType?

Playing further with this, I've noticed that the following does compile and work:

impl<BP> BufferUser<BP> where
    for <'a> BP: BufferProvider<'a, BufferType=Buffer<'a>>,

But unfortunately it limits the block to one particular type of buffer, while I need to cover all implementors of Clearable...

Also according to the RFC for associated type bounds, the syntax I am using seems to be expected for what I am trying to do, except maybe the part with the lifetime?

Looks like I just happen to be hitting this issue. :frowning: The original question being answered, I think we can close this thread. I may reopen another one if I cannot find a workaround.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.