Chapter 18: Lifetimes, Relationships Not Durations
Prerequisites
You will understand
- Lifetimes as relationship contracts, not durations
- The three elision rules and when to annotate
- Why
'staticdoes not mean "lives forever"
Reading time
45 min
+ 25 min exercises
Builds on Chapter 11
A lifetime annotation makes explicit what the compiler already checks: every reference must be valid for as long as it is used.
Revisit Ch 11 →
You'll need this for Chapter 39
Ch 39 explores advanced lifetime patterns: higher-ranked trait bounds, lifetime variance, and subtyping — all built on the model you learn here.
Ch 39: Lifetimes in Depth →
Core Diagram
Lifetimes as Relationships Between Valid Regions
Elision Rules
What the Compiler Infers for You
`'static`
Valid for the Whole Program
Chapter Resources
- Official Source: The Rust Reference: Lifetimes
- Rustonomicon: Lifetimes
- Rust by Example: Lifetimes
#![allow(unused)]
fn main() {
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
let result;
let s1 = String::from("long");
{
let s2 = String::from("hi");
result = longest(&s1, &s2); // 'a = shorter of s1, s2
} // s2 dropped — result would dangle
// println!("{result}"); // E0597: s2 doesn't live long enough
}
'a annotation
"The return value lives at most as long as both inputs." Not a duration — a relationship constraint.
Both inputs tied
Compiler unifies
'a to the shorter of the two lifetimes.
E0597
s2 is dropped at }. result might hold a reference to s2, so compiler rejects.
In Your Language: Lifetimes vs Garbage Collection
Rust — explicit lifetime annotations
#![allow(unused)]
fn main() {
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Compiler verifies both inputs outlive the return value
}
Go — GC handles it
func longest(x, y string) string {
if len(x) > len(y) { return x }
return y
}
// No annotation needed — GC keeps both alive
// But: unpredictable pause times, higher memory usage
Readiness Check - Lifetime Reasoning
Use this checkpoint to confirm you can reason about reference relationships, not just syntax.
| Skill | Level 0 | Level 1 | Level 2 | Level 3 |
|---|---|---|---|---|
| Explain what a lifetime means | I think it is a time duration | I know it describes validity scope | I can explain it as a relationship between borrows and owners | I can teach why annotations do not extend object lifetime |
| Read lifetime signatures | I avoid annotated signatures | I can parse single-input/output signatures | I can explain multi-input relationships like longest<'a> | I can redesign signatures to express clearer borrow contracts |
| Diagnose lifetime errors | I guess and add annotations randomly | I can recognize outlives problems | I can pinpoint the dropped owner causing E0597/E0515 | I can choose when returning owned values is the better design |
If any row is below Level 2, revisit Chapter 11 and run Drill Deck 2 again.
Compiler Error Decoder - Lifetime Relationships
| Error code | What it usually means | Typical fix direction |
|---|---|---|
| E0597 | Referenced value does not live long enough | Move owner to a wider scope or return owned data instead |
| E0515 | Returning a reference to local data | Return an owned value, or borrow from caller-provided inputs |
| E0621 | Function signature lifetime contract mismatches implementation | Align annotations with real input-output borrow relationship |
Treat lifetime errors as relationship mismatches, not annotation shortages.
Step 1 - The Problem
Learning Objective By the end of this chapter, you should be able to explain how lifetimes define relationships between borrowed data, rather than magically extending how long data exists.