Variable is `'static` but "does not live long enough"

Why am I not allowed to borrow bar for 'a if bar is of type Bar, which is 'static?

struct Foo<'a, Bar>(&'a Bar);

impl<'a, Bar: 'static> Foo<'a, Bar> {
    fn help(bar: Bar) {
        let _bar: &'a Bar = &bar;
    }
}

fn main() {}

Playground.

On one hand, you need to make sure you don’t fall for the misconception that Bar: 'static means every value of type Bar lives forever. Maybe that’s the issue in your understanding here, maybe not. Follow the link to a good longer explanation about that misconception; the : 'static only says something about the type Bar (namely, that value of this type can be kept around for arbitrarily long; most basic types in Rust fulfill T: 'static), not about how long your concrete value bar: Bar actually exists.

The other key information here is that lifetime parameters always refer to borrows outside of function’s scopes. So in a function like foo<'a>(…) or impl<'a, …> fn foo(…) (the latter is the case here), a local variable can never be borrowed for the lifetime 'a; on the other hand, this goes hand in hand with the fact that every reference you get as an input, likex_ref in the signature fn foo<'a>(x_ref: &'a Type), will be valid to use for the entire duration of the function call (and this value can even be used in return values if the lifetime 'a is involved in the return type).

I.e. the key here is that lifetime parameters of functions, like 'a here, refers to a lifetime that ends after the function call is over. The workaround in cases where you have to refer to a lifetime of a local borrow in type signatures, commonly necessary when you need to express a trait bound involving this lifetime, is to use a HRTB[1]; your concrete code example seems to only be a toy example so far… and if you intend to expand it to where the borrow of bar is returned as part of a Foo<'_> value, then that’s clearly illegal and unsound and thus can’t work, as it would be returning a reference to a local variable even after it’s dropped.


  1. for example, something like for<'a> &'a T: IntoIterator<Item = &'a u8> allows you for a value t: T to write for x /*: &u8 */ in &t { … } because the trait bound then also applies to the type of the local borrow &t ↩︎

4 Likes

I think this might've been a bit of an x-y post from me. My actual code looks like

struct Foo<'a, Bar>(&'a Bar);

impl<'a, Bar: 'static> Foo<'a, Bar> {
    fn help(bar: Bar) {
        let borrowed_bar = &bar; 
        let foo = Self(borrowed_bar);
        // Do something with `foo`..
    }
}

If the lifetime of borrowed_bar is 'method, I didn't understand why I couldn't create a Foo<&'method Bar>. I now realized that the mistake was using Self instead of Foo, because the former carries the 'a bound.

4 Likes

Yes, that’s a tricky little trap, using Self (especially as a constructor) in an associated function when you actually needed to use the same time but with a different lifetime.

4 Likes