Lifetimes

Lifetime Bounds

A lifetime bound constrains either a lifetime parameter ('b: 'a, read as "'b outlives 'a") or a generic type parameter (T: 'a, read as "any references inside T live at least as long as 'a").

Outlives Bounds Between Lifetimes ('b: 'a)

The syntax 'b: 'a declares that lifetime 'b is at least as long as (a superset of) lifetime 'a. Therefore, any reference &'b T can be safely coerced into &'a T:

Timeline for `'b: 'a`:
  'b (longer):  |--------------------------------------|
  'a (shorter):       |------------------------|
  Every point in `'a` is also valid in `'b`.
// `'b: 'a` guarantees `'b` lives at least as long as `'a`,
// so `&'b str` can be returned where `&'a str` is expected.
fn choose_str<'a, 'b: 'a>(first: &'a str, second: &'b str, pick_first: bool) -> &'a str {
    if pick_first {
        first
    } else {
        second // `&'b str` coerces to `&'a str` because `'b: 'a`
    }
}

fn main() {
    let longer = String::from("longer-lived string");
    {
        let shorter = String::from("shorter-lived string");
        let chosen = choose_str(&shorter, &longer, false);
        println!("Chosen: {chosen}");
    }
}
Chosen: longer-lived string

Type-Outlives Bounds (T: 'a)

When a generic type T is stored behind a reference &'a T or inside a struct with lifetime 'a, any references contained inside T itself must also remain valid for at least 'a. This constraint is written T: 'a:

  • If T is an owned type with no non-'static references (such as i32, String, or Vec<u8>), T: 'a is satisfied for every lifetime 'a (including 'static).
  • If T is a reference or contains a reference (such as &'b str), T: 'a holds if and only if 'b: 'a.
use std::fmt::Display;

// Modern Rust infers `T: 'a` automatically whenever `&'a T` appears in a struct field,
// or you can write it explicitly in a `where` clause:
struct RefWrapper<'a, T: 'a> {
    reference: &'a T,
}

fn print_wrapped<'a, T>(wrapper: RefWrapper<'a, T>)
where
    T: Display + 'a,
{
    println!("Wrapped value: {}", wrapper.reference);
}

fn main() {
    let number = 42;
    let wrapped = RefWrapper { reference: &number };
    print_wrapped(wrapped);
}
Wrapped value: 42

The T: 'static Bound

A special and common case of T: 'a is T: 'static, which is required by APIs like std::thread::spawn and std::any::Any.

T: 'static does not mean the value must be a &'static reference; it means T contains no non-'static borrowed references. Owned types like String, Vec<i32>, and u64 all satisfy T: 'static:

Type T Satisfies T: 'static? Why
i32, String, Vec<T> (where T: 'static) Yes Owns all its data; depends on no temporary stack borrows
&'static str Yes Borrow is valid for the entire program execution
&'a str (where 'a is a local scope) No Contains a temporary borrow 'a that expires before 'static
fn require_static_bound<T: 'static + std::fmt::Debug>(value: T) {
    println!("Accepted 'static-bounded value: {value:?}");
}

fn main() {
    // 1. Owned String created at runtime satisfies `T: 'static`!
    let owned = String::from("I am a freshly allocated String");
    require_static_bound(owned);

    // 2. &'static str literal also satisfies `T: 'static`:
    require_static_bound("I am a string literal");
}
Accepted 'static-bounded value: "I am a freshly allocated String"
Accepted 'static-bounded value: "I am a string literal"