Lifetime bounds to indicate type is owned

I have a lifetime problem that I can't seem to crack. An example of my problem is here. Essentially I have a method that returns an instance of T (which I intended to always be owned) that the method creates based on the contents of a buffer.

    pub fn read<'this, 'bytes, T>(&'this mut self) -> Result<T, ()>
    where
        T: TryFrom<&'bytes [u8], Error = ()> + 'static,
    {
        let buf = self.buffer.fill_buf().unwrap();
        let size = buf.len();
        let result = T::try_from(buf); // Compiler-error
        self.buffer.consume(size);
        result
    }

It continues to fail with

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> test2.rs:38:31
   |
38 |         let buf = self.buffer.fill_buf().unwrap();
   |                               ^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'this` as defined on the method body at 34:17...
  --> test2.rs:34:17
   |
34 |     pub fn read<'this, 'bytes, T>(&'this mut self) -> Result<T, ()>
   |                 ^^^^^
note: ...so that reference does not outlive borrowed content
  --> test2.rs:38:19
   |
38 |         let buf = self.buffer.fill_buf().unwrap();
   |                   ^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'bytes` as defined on the method body at 34:24...
  --> test2.rs:34:24
   |
34 |     pub fn read<'this, 'bytes, T>(&'this mut self) -> Result<T, ()>
   |                        ^^^^^^
note: ...so that reference does not outlive borrowed content
  --> test2.rs:40:34
   |
40 |         let result = T::try_from(buf); // Compiler-error
   |                                  ^^^

Now that make sense if a T actually holds a reference to buf but my intention is that T should always be owned (and this works if I hard code an owned type in place of T). I attempted to communicate this to the compiler by adding the 'static lifetime bound but that doesn't stop a T from containing references, it only ensures that those reference are 'static, which of course the reference to buf will not satisfy.

So is there a bound I can place on T to ensure that it is always owned (never holds a reference to the buf)? Or is there some better way to structure this code?

Thanks in advance!

The problem is that, as written, 'bytes is chosen by the caller in the sense that I can decide to call the function

something.read::<'something, 'static, i32>();

However, all your method now knows is that T implements TryFrom<&'static [u8], Error = ()>. This is a problem, because buf is an &'some_short_lifetime [u8], and not an &'static [u8], so you can't pass it to the T::try_from method.

To fix this, do this instead:

pub fn read<'this, T>(&'this mut self) -> Result<T, ()>
where
    T: for<'bytes> TryFrom<&'bytes [u8], Error = ()> + 'static,
{
    let buf = self.buffer.fill_buf().unwrap();
    let size = buf.len();
    let result = T::try_from(buf);
    self.buffer.consume(size);
    result
}

The for<'a> is a special shorthand syntax for writing the following infinite list of where bounds:

where
    T: TryFrom<&'lifetime1 [u8], Error = ()> + 'static,
    T: TryFrom<&'lifetime2 [u8], Error = ()> + 'static,
    T: TryFrom<&'lifetime3 [u8], Error = ()> + 'static,
    T: TryFrom<&'lifetime4 [u8], Error = ()> + 'static,
    T: TryFrom<&'lifetime5 [u8], Error = ()> + 'static,
    ...

and so on for every possible lifetime. Since the list has all lifetimes, it also has the lifetime that buf is annotated with somewhere, hence you are allowed to call T::try_from(buf).

Only borrows have lifetimes, so you can't do it in the direct sense you're asking for. Lifetimes are simply not applicable to self-contained/owned types.

'static doesn't mean a type is owned. It's just the most restrictive lifetime for borrows, so it rejects all temporary borrows, leaving whole-program-long borrows, and owned types which ignore such annotations.


But in your case it's not the problem that T isn't owned, but the problem that you promise try_from arg to be a buffer that the try_from function can keep for a long time (not necessarily in T, but maybe magically pass it to some other place).

In your case you need: T: for<'a> TryFrom<&'a [u8], Error = ()>

pub fn read<'bytes, T>(&mut self) -> Result<T, ()>
    where
    	T: for<'a> TryFrom<&'a [u8], Error = ()> + 'static,
    {
    	let buf = self.buffer.fill_buf().unwrap();
    	let size = buf.len();
    	let result = T::try_from(buf); // COMPILER ERROR
    	self.buffer.consume(size);
    	result
    }

This is because if you use a lifetime that is defined somewhere outside of function's definition, you can't pass just any buffer to the function call. You have to pass the very specific buffer indicated by this very specific lifetime, and if the lifetime exists outside of the function, then the buffer has to exist outside of the function too.

The for<'a> annotation effectively creates a new lifetime/scope every time you use it. Or another way to look at it, that it's supposed to be valid for any lifetime you use it with. So it allows the buffer to be valid only for TryFrom function call, and no longer.

1 Like

Thanks very much, @alice and @kornel! That definitely solves my problem. Higher-rank trait bounds have always been a bit confusing to me, but your explanations help a lot.