Lifetime bounds describe how references remain valid. Two common forms answer different questions:
| Bound | Meaning |
|---|---|
'a: 'b |
Lifetime 'a outlives 'b |
T: 'a |
The type T satisfies the requirements to outlive 'a |
These bounds constrain relationships. They do not extend the lifetime of a local value or change when it is dropped.
One lifetime outlives another
'a: 'b means 'a lasts at least as long as 'b. A shared reference
valid for 'a can be used where a shorter 'b reference is required.
fn shorten<'a, 'b>(text: &'a str) -> &'b str
where
'a: 'b,
{
text
}
fn main() {
let text = String::from("ready");
assert_eq!(shorten(&text), "ready");
}
The bound points from the longer lifetime to the shorter one. It permits shortening a reference's usable lifetime, not extending it past the data.
A type outlives a lifetime
For ordinary reference-containing types, T: 'a means their borrowed
data must remain valid for 'a. Examples:
| Type bound | Does it hold? |
|---|---|
i32: 'static |
Yes |
String: 'static |
Yes |
Vec<String>: 'static |
Yes |
&'static str: 'a |
Yes, for any 'a |
Vec<&'a str>: 'static |
Only when 'a outlives 'static |
Owning a collection does not necessarily make its type 'static: an
owned vector can contain borrowed strings. A type such as String
contains no borrowed data, so it satisfies 'static even when a particular
string value is created and dropped inside a short function.
fn accept_static_type<T: 'static>(value: T) {
drop(value);
}
fn main() {
let text = String::from("temporary");
accept_static_type(text);
}
This bound does not keep text alive forever. It allows the function to
own a value whose type has no shorter-lived borrow preventing long-term
storage. The function can still drop it immediately.
A parameter of type &'static String is different: it asks for a reference
valid for 'static. Borrowing an ordinary local string does not satisfy
that requirement.
Check your understanding Does (i32, &'a str) satisfy 'static? Show answer Hide answer
Only if the borrowed string's lifetime also satisfies 'static.
The integer does not impose a shorter lifetime, but the tuple still contains
the reference. Wrapping a borrow in a tuple does not remove its lifetime
requirement.
Bounds implied by reference types
The type &'a T requires T: 'a. When it appears in a function
signature, the function body can rely on that relationship without spelling
out the bound separately:
pub fn require_outlives<'a, T: 'a>(marker: &'a ()) -> &'a () { marker }
fn inspect<'a, T>(value: &'a T, marker: &'a ()) -> &'a T {
require_outlives::<'a, T>(marker);
value
}
fn main() {
let value = String::from("sample");
let marker = ();
assert_eq!(inspect(&value, &marker), "sample");
}
The same well-formedness requirement lets a struct field such as
value: &'a T supply an inferred outlives bound. The optional
explicit_outlives_requirements lint can identify redundant bounds on
type definitions. It is not a rule to delete every lifetime bound.
When the signature supplies no relationship
If a function receives only &'a (), that type says nothing about T.
A call requiring T: 'a then needs the relationship declared explicitly:
pub fn require_outlives<'a, T: 'a>(marker: &'a ()) -> &'a () { marker }
fn inspect_marker<'a, T: 'a>(marker: &'a ()) -> &'a () {
require_outlives::<T>(marker)
}
fn main() {
let marker = ();
inspect_marker::<String>(&marker);
}
Removing T: 'a from this wrapper leaves the call without the required
bound. E0309 reports that the generic type may not live long enough for the
named lifetime. An analogous example with an elided lifetime can produce
E0311. Read the required relationship in the diagnostic before deciding
whether to change the signature or the operation.
Trait bounds are a separate requirement
A reference to T does not imply T: Display. If generic code formats
an arbitrary T, that capability must be available through its declared
bounds. Rust does not infer a function's trait requirements just by examining
the operations in its body.
This does not mean every trait bound must be repeated everywhere.
A declared trait's supertraits and surrounding implementation bounds can
already provide the requirement. Types can also require trait bounds to
be well formed; mentioning such a type does not automatically grant the
bound in the same way that &'a T grants T: 'a.
When a trait-bound error appears, adding a bound is one possible fix. Changing an unnecessarily restrictive operation may be better. Lifetime errors likewise require understanding which data is borrowed and how long it is needed, rather than mechanically adding or removing annotations.
References: lifetime and implied bounds and E0311.