Lifetime Parameters
A lifetime parameter ('a, 'b) is a generic parameter declared in angle brackets <'a> that abstracts over the concrete borrow duration of references passed to a function or stored in a type. Just as a generic type parameter <T> lets an item work with any concrete type, a lifetime parameter <'a> lets an item work with references of any concrete lifetime that satisfies the declared constraints.
Where Lifetime Parameters Are Declared
Lifetime parameters are declared inside <...> on five kinds of Rust constructs, and when combined with generic type parameters (T, U), lifetime parameters always come first (<'a, 'b, T, U>):
| Construct | Declaration Syntax | Example |
|---|---|---|
| Function | fn name<'a>(...) |
fn first<'a, T>(slice: &'a [T]) -> Option<&'a T> |
| Struct | struct Name<'a> { ... } |
struct View<'a> { data: &'a str } |
| Enum | enum Name<'a> { ... } |
enum Token<'a> { Ident(&'a str), Number(i64) } |
impl Block |
impl<'a> Name<'a> { ... } |
impl<'a> View<'a> { fn len(&self) -> usize { ... } } |
| Trait / Type Alias | trait Name<'a> / type Alias<'a> |
type StrPair<'a> = (&'a str, &'a str); |
#[derive(Debug)]
struct KeyValue<'a, V> {
key: &'a str,
value: V,
}
fn make_entry<'a, V>(key: &'a str, value: V) -> KeyValue<'a, V> {
KeyValue { key, value }
}
fn main() {
let raw_key = String::from("timeout_ms");
let entry: KeyValue<'_, u32> = make_entry(&raw_key, 5000);
println!("Entry: {} = {}", entry.key, entry.value);
}
Entry: timeout_ms = 5000
Independent vs Tied Lifetime Parameters (<'a> vs <'a, 'b>)
If a function takes two reference parameters (x and y) and only returns data borrowed from x, you should not tie y to the same lifetime parameter 'a!
- If you write
fn pick_first<'a>(x: &'a str, y: &'a str) -> &'a str, the returned reference is unnecessarily restricted byy's lifetime too. - If you give
yits own independent lifetime parameter'b(or omity's lifetime so elision gives it an independent lifetime), the returned reference&'a strdepends only onx:
With Independent Lifetimes `fn pick_first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str`:
x ('a): |========================================|
y ('b): |==========| (y is dropped at `}`)
return: |========================================| <-- Can still be used after `y` drops!
// Only `x` shares `'a` with the return type; `y` has an independent lifetime `'b`:
fn strip_known_prefix<'a, 'b>(text: &'a str, prefix: &'b str) -> &'a str {
match text.strip_prefix(prefix) {
Some(rest) => rest, // `rest` is a sub-slice of `text` (`'a`), NOT `prefix` (`'b`)!
None => text,
}
}
fn main() {
let document = String::from("PREFIX:important payload");
let payload: &str;
{
let temporary_prefix = String::from("PREFIX:");
payload = strip_known_prefix(&document, &temporary_prefix);
} // `temporary_prefix` (`'b`) is dropped here!
// Valid! Because `payload` has lifetime `'a` (tied only to `document`),
// it remains valid even after `temporary_prefix` was destroyed:
println!("Extracted payload: '{payload}'");
}
Extracted payload: 'important payload'
Unused Lifetime Parameter Error (E0392)
Every lifetime parameter 'a declared on a struct or enum (struct Foo<'a>) must be used in at least one field of that struct or enum (E0392 if unused):
// ❌ does not compile
struct Unused<'a> {
count: u32,
}
fn main() {}
error[E0392]: lifetime parameter `'a` is never used
--> src/main.rs:1:15
|
1 | struct Unused<'a> {
| ^^ unused lifetime parameter
|
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
error: aborting due to 1 previous error