Skip to content
← Back to explainers

Rust explainer

Outlives bounds: 'a: 'b, T: 'a, and implied bounds

About 4 min to read. Allow extra time to work through examples.

Read lifetime relationships, distinguish a type's lifetime bounds from a value's lifetime, and learn which bounds Rust can infer from a signature.

This article uses Rust. You can make it your practice mode to browse related content across the site.

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

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.

step 21 of 22

Ownership III: Lifetimes, Explicitly

Up next Generics, traits and lifetimes in one signature