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