Lifetimes

Why a returned reference needs a named lifetime, elision, and the patterns that keep borrows shorter than the data they point at.

Lifetimes

A reference must not outlive its data. You have been obeying that since borrows. This lesson is the syntax for the cases the compiler cannot infer, then the patterns that keep those annotations small. Closures come after, because they capture references and need this rule.

Naming a borrow

A lifetime is the region of code where a reference is valid. Most of them are inferred. You write one when a function returns a reference and the compiler cannot tell which input it came from. fn longest(x: &str, y: &str) -> &str fails for that reason. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str says the result lives only as long as both inputs, which means as long as the shorter one.

lifetime 'athe overlapx"abcd"y"xyz"resultone of them
StatusBoth inputs are borrowed for 'a

&'a str on each parameter means neither borrow ends while 'a is still in use.

Elision is why many signatures need no annotation.

SignatureLifetime the compiler fills in
Each input referenceIts own lifetime
One input lifetimeThat lifetime is used for the outputs
A method with &self or &mut selfThat lifetime is used for the outputs

So fn first_word(s: &str) -> &str is already clear. 'static means the reference lives for the whole program: a string literal or a static. It does not mean “leak this.” Forcing 'static to silence an error usually hides the shorter lifetime you actually have. Prefer an owned String in a struct unless you are deliberately looking at a buffer without copying.

01_lifetime_basics.rsRust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

struct Excerpt<'a> {
    part: &'a str,
}

fn main() {
    let a = String::from("abcd");
    let b = String::from("xyz");
    let word = longest(&a, &b);
    let excerpt = Excerpt { part: &a };
    println!("{} {}", word, excerpt.part);
}

Patterns

SituationWhat to do
A long-lived structOwn the data. A borrowed field ties the struct to some other buffer. Zero-copy parsers do that on purpose, and only then.
You need to mutate the owner againEnd the temporary borrow in a smaller block first.
The output might be either inputGive both inputs the same lifetime.
The output is always the first inputName only that lifetime.
An erased error that crosses threadsBox of dyn Error plus Send plus Sync plus 'static. The error does not borrow a short-lived local. You will use it when you spawn work, not before.
A function that works for any lifetimefor<'a> Fn(&'a str) -> &'a str. You will see it on library signatures. You do not need to write it yet.
name: Stringindependentsource buffermust stayname: &'a strview
StatusOwned data has no lifetime parameter

struct User { name: String } can move freely. This is the default for domain types.

02_lifetime_patterns.rsRust
fn first_word(s: &str) -> &str {
    s.split_whitespace().next().unwrap_or(s)
}

fn pick_first<'a>(primary: &'a str, _other: &str) -> &'a str {
    primary
}

fn main() {
    let sentence = String::from("hello rust");
    let word = first_word(&sentence);
    println!("{}", pick_first(word, "fallback"));
}