Lifetimes

Lifetime Annotations

A lifetime annotation (such as 'a, 'b, or 'src) is a compile-time label written on a reference type (&'a T or &'a mut T) that describes how the lifetimes of multiple references relate to one another. Importantly, a lifetime annotation never changes how long a value lives at runtime; instead, it gives the borrow checker a symbolic name to verify that references remain valid for the required span.

Lifetime Annotation Syntax

Lifetime names always start with an apostrophe (') followed by a short lowercase identifier (typically 'a, 'b, 'c, or a descriptive name like 'input or 'ctx). Inside a reference type, the lifetime annotation is placed immediately after the & symbol and before mut (if present) or the target type T:

Reference Kind Without Lifetime Annotation With Lifetime Annotation 'a
Shared reference &i32 &'a i32
Mutable reference &mut i32 &'a mut i32
Shared string slice &str &'a str
Mutable array slice &mut [u8] &'a mut [u8]
&'a mut Vec<String>
─ ── ─── ───────────
│ │   │       └── Target type being borrowed
│ │   └────────── Mutability modifier (comes AFTER `'a`)
│ └────────────── Lifetime annotation (`'a`)
└──────────────── Reference operator

Declaring Lifetime Parameters in Angle Brackets (<'a>)

Just like generic type parameters (<T>), a lifetime name 'a (other than the built-in 'static and anonymous '_ lifetimes) must be declared inside angle brackets <'a> on the function, struct, enum, or impl block before it can be used in parameter, field, or return types:

// `'a` is declared in `first_line<'a>` and used on both the parameter and return type:
fn first_line<'a>(document: &'a str) -> &'a str {
    match document.lines().next() {
        Some(line) => line,
        None => "",
    }
}

fn main() {
    let text = String::from("Header line\nSecond line\nThird line");
    let header: &str = first_line(&text);

    println!("Extracted header: '{header}'");
}
Extracted header: 'Header line'

What a Shared Lifetime Label 'a Actually Means

When the same lifetime label 'a appears on two input references (x: &'a str, y: &'a str) and on a return reference (-> &'a str), x and y do not need to have the exact same scope in the caller!

Instead, 'a resolves to the intersection (the smaller, overlapping portion) of x's lifetime and y's lifetime:

Caller Scope:
  x lives for `'long`:   |========================================|
  y lives for `'short`:          |==================|
  'a = overlap of both:          |==================|  <-- Returned `&'a str` is valid for `'short`!
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() >= y.len() { x } else { y }
}

fn main() {
    let outer = String::from("long outer string"); // `'long`

    {
        let inner = String::from("short"); // `'short`
        let chosen = longer(&outer, &inner);

        // Valid here because `'a` is the overlap (`'short`), and `inner` is still alive:
        println!("Chosen inside inner block: '{chosen}'");
    }
}
Chosen inside inner block: 'long outer string'

The Anonymous Lifetime ('_)

Rust also provides a special placeholder lifetime written '_ (called the anonymous lifetime or elided lifetime). You can use '_ without declaring it in <...> to tell the compiler "infer the lifetime here using standard elision rules" while still making it visually clear in the code that a struct or return type borrows data:

struct Words<'a> {
    remaining: &'a str,
}

// Using `Words<'_>` in the return type avoids declaring `<'a>` on `wrap_words`!
fn wrap_words(input: &str) -> Words<'_> {
    Words { remaining: input }
}

fn main() {
    let w = wrap_words("hello world");
    println!("Wrapped: '{}'", w.remaining);
}
Wrapped: 'hello world'

Common Errors

Using an Undeclared Lifetime Name (E0261)

Writing 'a in a parameter or return type without declaring <'a> after the function or struct name fails with E0261:

// ❌ does not compile
fn trim_prefix(text: &'a str) -> &'a str {
    text.trim_start()
}

fn main() {}
error[E0261]: use of undeclared lifetime name `'a`
 --> src/main.rs:1:23
  |
1 | fn trim_prefix(text: &'a str) -> &'a str {
  |                       ^^ undeclared lifetime
  |
help: consider introducing lifetime `'a` here
  |
1 | fn trim_prefix<'a>(text: &'a str) -> &'a str {
  |               ++++

error: aborting due to 2 previous errors