Lifetime Bounds
A lifetime bound constrains either a lifetime parameter ('b: 'a, read as "'b outlives 'a") or a generic type parameter (T: 'a, read as "any references inside T live at least as long as 'a").
Outlives Bounds Between Lifetimes ('b: 'a)
The syntax 'b: 'a declares that lifetime 'b is at least as long as (a superset of) lifetime 'a. Therefore, any reference &'b T can be safely coerced into &'a T:
Timeline for `'b: 'a`:
'b (longer): |--------------------------------------|
'a (shorter): |------------------------|
Every point in `'a` is also valid in `'b`.
// `'b: 'a` guarantees `'b` lives at least as long as `'a`,
// so `&'b str` can be returned where `&'a str` is expected.
fn choose_str<'a, 'b: 'a>(first: &'a str, second: &'b str, pick_first: bool) -> &'a str {
if pick_first {
first
} else {
second // `&'b str` coerces to `&'a str` because `'b: 'a`
}
}
fn main() {
let longer = String::from("longer-lived string");
{
let shorter = String::from("shorter-lived string");
let chosen = choose_str(&shorter, &longer, false);
println!("Chosen: {chosen}");
}
}
Chosen: longer-lived string
Type-Outlives Bounds (T: 'a)
When a generic type T is stored behind a reference &'a T or inside a struct with lifetime 'a, any references contained inside T itself must also remain valid for at least 'a. This constraint is written T: 'a:
- If
Tis an owned type with no non-'staticreferences (such asi32,String, orVec<u8>),T: 'ais satisfied for every lifetime'a(including'static). - If
Tis a reference or contains a reference (such as&'b str),T: 'aholds if and only if'b: 'a.
use std::fmt::Display;
// Modern Rust infers `T: 'a` automatically whenever `&'a T` appears in a struct field,
// or you can write it explicitly in a `where` clause:
struct RefWrapper<'a, T: 'a> {
reference: &'a T,
}
fn print_wrapped<'a, T>(wrapper: RefWrapper<'a, T>)
where
T: Display + 'a,
{
println!("Wrapped value: {}", wrapper.reference);
}
fn main() {
let number = 42;
let wrapped = RefWrapper { reference: &number };
print_wrapped(wrapped);
}
Wrapped value: 42
The T: 'static Bound
A special and common case of T: 'a is T: 'static, which is required by APIs like std::thread::spawn and std::any::Any.
T: 'static does not mean the value must be a &'static reference; it means T contains no non-'static borrowed references. Owned types like String, Vec<i32>, and u64 all satisfy T: 'static:
Type T |
Satisfies T: 'static? |
Why |
|---|---|---|
i32, String, Vec<T> (where T: 'static) |
Yes | Owns all its data; depends on no temporary stack borrows |
&'static str |
Yes | Borrow is valid for the entire program execution |
&'a str (where 'a is a local scope) |
No | Contains a temporary borrow 'a that expires before 'static |
fn require_static_bound<T: 'static + std::fmt::Debug>(value: T) {
println!("Accepted 'static-bounded value: {value:?}");
}
fn main() {
// 1. Owned String created at runtime satisfies `T: 'static`!
let owned = String::from("I am a freshly allocated String");
require_static_bound(owned);
// 2. &'static str literal also satisfies `T: 'static`:
require_static_bound("I am a string literal");
}
Accepted 'static-bounded value: "I am a freshly allocated String"
Accepted 'static-bounded value: "I am a string literal"