17.1What lifetimes are

Last updated June 13, 2026

Lifetimes have a fearsome reputation, and it's mostly undeserved. Here's the reassuring truth up front: you've been using lifetimes correctly since chapter 9, the compiler just wrote them for you. A lifetime is the span of program during which a reference is valid, and the borrow checker has been tracking them all along to enforce its rule that "references must always be valid" (lesson 9.5). This chapter teaches the rare cases where you write a lifetime down by hand, and it's deliberately short, because most lifetime knowledge is "the compiler handles it."

A lifetime is a span of validity

Every reference points at some value, and that value lives for a certain stretch of the program (lesson 8.3: a value is dropped when its owner leaves scope). A reference is valid only while the value it points at is alive. That window, "from when this reference is created to the last point it must still be valid", is its lifetime. You met the idea precisely in lesson 9.5: a reference must never outlive the value it borrows, or it would dangle, pointing at freed memory.

fn main() {
    let r;                  // r will hold a reference
    {
        let x = 5;
        r = &x;             // r borrows x
    }                       // x is dropped here
    println!("{r}");        // using a dangling reference: refused
}
error[E0597]: `x` does not live long enough
 --> src/main.rs:5:13
  |
4 |         let x = 5;
  |             - binding `x` declared here
5 |         r = &x;
  |             ^^ borrowed value does not live long enough
6 |     }
  |     - `x` dropped here while still borrowed
7 |     println!("{r}");
  |                --- borrow later used here

x lives only inside the inner block; r is meant to outlive it. The borrow checker compares the two lifetimes, the short one for x and the longer one r would need, and sees that r would point at x after x is gone. So it refuses, with E0597, "x does not live long enough." That comparison of lifetimes is what the borrow checker has been doing on every reference since chapter 9. The concept isn't new; only the name is.

Key insight

A lifetime isn't a thing you create or a duration you set. It's a description of how long a reference is valid, derived from the scopes of the values involved. The borrow checker computes lifetimes and checks that no reference outlives its value. Lifetime annotations, the 'a syntax this chapter introduces, don't change how long anything lives; they only describe relationships between lifetimes to the compiler in the few cases it can't work them out alone. You're labeling existing facts, not setting timers.

Why you've rarely seen them written

If lifetimes are on every reference, why haven't you written one in nine chapters? Because the compiler infers them in the overwhelming majority of cases, through rules called elision (lesson 17.4) that let it fill in the obvious lifetimes silently. Every &str parameter, every &self method, every borrow you've passed to a function had a lifetime; the compiler just didn't make you spell it out, the same way it infers most types without annotations (lesson 4.9).

You write a lifetime by hand only when the compiler genuinely can't tell how two references relate, and there are really two situations: a function that returns a reference, where the compiler needs to know which input the output borrows from (lesson 17.2), and a struct that holds a reference, the case lesson 10.8 deferred to this chapter (lesson 17.3). Outside those, you'll mostly keep not writing them.

The vocabulary you've already met

Two pieces of lifetime vocabulary have already appeared. In lesson 9.5 and lesson 9.7, you saw 'static, the lifetime of string literals, which live for the entire program (the whole-program span; lesson 17.5 returns to it). And in lesson 10.8, trying to put a &str in a struct produced E0106, "missing lifetime specifier," with the compiler suggesting <'a>, the exact annotation this chapter teaches you to read and write. Those weren't detours; they were this chapter's groundwork, dropped early so the syntax would feel familiar when it arrived.

Best practice

Don't go looking for lifetimes to write. Write your code naturally, prefer owned data in structs (lesson 10.8), and let the compiler infer lifetimes on function references. When the compiler asks for an annotation (E0106, "missing lifetime specifier", or "does not live long enough"), then reach for the tools in this chapter. Lifetimes are a thing you add in response to a specific compiler request, not a decoration you sprinkle preemptively.

Quiz time

Question #1

In one sentence, what is a lifetime?

Show solution

A lifetime is the span of the program during which a reference is valid, derived from how long the value it points at lives. The borrow checker uses lifetimes to enforce that no reference outlives its value (lesson 9.5).

Question #2

Why does the inner-block example fail to compile, in lifetime terms?

Show solution

x lives only inside the inner block, so its lifetime is short. r is declared in the outer scope and would need to be valid longer, but it borrows x. The borrow checker sees r's required lifetime exceeds x's actual lifetime, meaning r would point at x after x is dropped (a dangling reference), so it refuses with E0597 ("x does not live long enough").

Question #3

If every reference has a lifetime, why have you almost never written one?

Show solution

Because the compiler infers lifetimes automatically in nearly all cases, through elision rules (lesson 17.4), just as it infers most types. You only write a lifetime by hand when the compiler can't determine how references relate, mainly when a function returns a reference or a struct holds one. Everywhere else, the lifetimes are present but implicit.

The first case where you must write one is a function that returns a reference. The next lesson introduces the 'a syntax on the longest function and shows exactly what the annotation promises (and, importantly, what it does not do).