Lifetimes

Lifetime Parameters

A lifetime parameter ('a, 'b) is a generic parameter declared in angle brackets <'a> that abstracts over the concrete borrow duration of references passed to a function or stored in a type. Just as a generic type parameter <T> lets an item work with any concrete type, a lifetime parameter <'a> lets an item work with references of any concrete lifetime that satisfies the declared constraints.

Where Lifetime Parameters Are Declared

Lifetime parameters are declared inside <...> on five kinds of Rust constructs, and when combined with generic type parameters (T, U), lifetime parameters always come first (<'a, 'b, T, U>):

Construct Declaration Syntax Example
Function fn name<'a>(...) fn first<'a, T>(slice: &'a [T]) -> Option<&'a T>
Struct struct Name<'a> { ... } struct View<'a> { data: &'a str }
Enum enum Name<'a> { ... } enum Token<'a> { Ident(&'a str), Number(i64) }
impl Block impl<'a> Name<'a> { ... } impl<'a> View<'a> { fn len(&self) -> usize { ... } }
Trait / Type Alias trait Name<'a> / type Alias<'a> type StrPair<'a> = (&'a str, &'a str);
#[derive(Debug)]
struct KeyValue<'a, V> {
    key: &'a str,
    value: V,
}

fn make_entry<'a, V>(key: &'a str, value: V) -> KeyValue<'a, V> {
    KeyValue { key, value }
}

fn main() {
    let raw_key = String::from("timeout_ms");
    let entry: KeyValue<'_, u32> = make_entry(&raw_key, 5000);

    println!("Entry: {} = {}", entry.key, entry.value);
}
Entry: timeout_ms = 5000

Independent vs Tied Lifetime Parameters (<'a> vs <'a, 'b>)

If a function takes two reference parameters (x and y) and only returns data borrowed from x, you should not tie y to the same lifetime parameter 'a!

  • If you write fn pick_first<'a>(x: &'a str, y: &'a str) -> &'a str, the returned reference is unnecessarily restricted by y's lifetime too.
  • If you give y its own independent lifetime parameter 'b (or omit y's lifetime so elision gives it an independent lifetime), the returned reference &'a str depends only on x:
With Independent Lifetimes `fn pick_first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str`:
  x ('a):  |========================================|
  y ('b):        |==========| (y is dropped at `}`)
  return:  |========================================|  <-- Can still be used after `y` drops!
// Only `x` shares `'a` with the return type; `y` has an independent lifetime `'b`:
fn strip_known_prefix<'a, 'b>(text: &'a str, prefix: &'b str) -> &'a str {
    match text.strip_prefix(prefix) {
        Some(rest) => rest, // `rest` is a sub-slice of `text` (`'a`), NOT `prefix` (`'b`)!
        None => text,
    }
}

fn main() {
    let document = String::from("PREFIX:important payload");
    let payload: &str;

    {
        let temporary_prefix = String::from("PREFIX:");
        payload = strip_known_prefix(&document, &temporary_prefix);
    } // `temporary_prefix` (`'b`) is dropped here!

    // Valid! Because `payload` has lifetime `'a` (tied only to `document`),
    // it remains valid even after `temporary_prefix` was destroyed:
    println!("Extracted payload: '{payload}'");
}
Extracted payload: 'important payload'

Unused Lifetime Parameter Error (E0392)

Every lifetime parameter 'a declared on a struct or enum (struct Foo<'a>) must be used in at least one field of that struct or enum (E0392 if unused):

// ❌ does not compile
struct Unused<'a> {
    count: u32,
}

fn main() {}
error[E0392]: lifetime parameter `'a` is never used
 --> src/main.rs:1:15
  |
1 | struct Unused<'a> {
  |               ^^ unused lifetime parameter
  |
  = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`

error: aborting due to 1 previous error