🎓 A1Learner
🔍

Rust Lifetime Bounds

A lifetime bound states an order between two lifetimes. The bound 'a: 'b says 'a outlives 'b.

Write the order: 'a: 'b

The bound sits with the letters. It reads "'a outlives 'b" — so the first input may safely be returned as the shorter-lived kind:

fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 {
    first
}

A longer lifetime shrinks to fit

Where 'a: 'b holds, a &'a value may be handed over as a &'b. Shrinking is always safe — stretching a short lifetime is never allowed:

fn shrink<'a: 'b, 'b>(r: &'a i32) -> &'b i32 { r }

Bound a type: T: 'a

The same idea aimed at a whole type — every reference inside T must outlive 'a:

struct Ref<'a, T: 'a>(&'a T);

A trait object silently defaults to 'static

Box<dyn Display> means Box<dyn Display + 'static> — an unwritten bound, not elision. A short borrow therefore cannot be boxed this way: the compiler refuses, saying the value must outlive 'static:

use std::fmt::Display;
fn boxed(s: &str) -> Box<dyn Display> {
    Box::new(s) // refused: returning this value requires that `s` outlive 'static
}

Name the bound to carry a shorter borrow

Add + 'a and the box may hold the borrow for exactly that long. This is the T: 'a idea from this lesson. Here it applies to a trait object instead of a struct field:

use std::fmt::Display;
fn boxed<'a>(s: &'a str) -> Box<dyn Display + 'a> {
    Box::new(s)
}
let local = String::from("short lived");
let b = boxed(&local);
println!("boxed = {}", b);

Example

#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
fn multiply<'a>(first: &'a i32, second: &'a i32) -> i32 {
    first * second
}
fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 {
    first
}
fn main() {
    let first = 2;
    {
        let second = 3;
        println!("product = {}", multiply(&first, &second));
        println!("first = {}", choose_first(&first, &second));
    }
    let x = 7;
    let held = Ref(&x);
    println!("held = {}", held.0);
}

Key points

  • When two references share one letter, Rust quietly infers the shortest lifetime that works for both
  • Combine the two with a plus: T: Debug + 'a means T implements Debug and all references inside T outlive 'a
  • Bounds can sit in the angle brackets, as in struct Ref<'a, T: 'a>.
  • Or they can sit in a where clause.
  • Both say the same thing.
  • A lifetime bound orders one lifetime against another.

In this example, first is made in main and second inside a block, so first outlives second. multiply shares one letter 'a, so Rust shrinks both references to the shorter lifetime and prints product = 6. choose_first states the order instead. 'a: 'b says the first input outlives the second. So returning it as a &'b i32 is safe. It prints first = 2. Below, Ref<'a, T: 'a> stores a &'a T. It bounds T so any reference inside T outlives 'a too. Holding &x prints held = 7.

Note

Rust sometimes refuses a borrow with error E0597.

The message is "borrowed value does not live long enough".

That is this same promise in plain words.

Some lifetime did not outlive another.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What does the bound 'a: 'b mean?