rustseverity: can-fix
E0106 / E0597 / E0623

Rust lifetime error — missing lifetime specifier / does not live long enough

missing lifetime specifier / does not live long enough

88% fixable~25 mindifficulty: advanced

Verified against The Rust Book: Chapter 10 (Lifetimes), The Rust Reference: Lifetime elision, rustc error index: E0106, E0597, E0623 · Updated June 2026

> quick_fix

Rust lifetime annotations tell the compiler how long references must remain valid. If you see 'does not live long enough', a reference is outliving the data it points to. If you see 'missing lifetime specifier', add a lifetime parameter `'a` to connect input and output references. Own the data (use `String` instead of `&str`) as the simplest fix.

// E0106: missing lifetime specifier
struct Parser {
    input: &str,  // ERROR: need a lifetime annotation
}

// Fix — add lifetime parameter
struct Parser<'a> {
    input: &'a str,  // lives as long as 'a
}

// Or — own the data, no lifetime needed
struct Parser {
    input: String,  // owns the string, no lifetime annotation needed
}

What causes this error

Rust's lifetime system ensures references never outlive the data they point to, preventing dangling pointers. Lifetime errors occur when: (1) a struct stores a reference without a lifetime annotation (E0106), (2) a local variable is referenced and then dropped while the reference is still alive (E0597), (3) a function returns a reference but the compiler can't determine its lifetime without an annotation (E0106), or (4) lifetime bounds conflict (E0623).

> advertisementAdSense placeholder

How to fix it

  1. 01

    step 1

    Understand what lifetime annotations mean

    Lifetime annotations don't change how long data lives — they describe relationships between reference lifetimes that the compiler uses to validate correctness. `'a` is just a name for 'some lifetime'. When a function takes `x: &'a str` and returns `&'a str`, it says: the output reference lives at most as long as the input.

    // Without annotation — compiler infers:
    fn longest(x: &str, y: &str) -> &str {  // ERROR: missing lifetime
        if x.len() > y.len() { x } else { y }
    }
    
    // With annotation — 'a means: output lives as long as the shorter of x and y
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() { x } else { y }
    }
  2. 02

    step 2

    Fix structs that hold references by adding lifetime parameters

    A struct that stores a reference must declare a lifetime parameter. The struct cannot outlive the reference it holds.

    // BAD — missing lifetime
    struct Important {
        part: &str,  // E0106
    }
    
    // GOOD — lifetime parameter
    struct Important<'a> {
        part: &'a str,
    }
    
    impl<'a> Important<'a> {
        fn announce(&self) -> &str {
            self.part
        }
    }
  3. 03

    step 3

    Fix 'does not live long enough' — the referenced data is dropped too early

    E0597 fires when a reference outlives the data it points to. The fix is to extend the data's lifetime (move it to an outer scope) or use an owned type instead of a reference.

    // BAD — result is a reference to text which is dropped at end of the block
    let result;
    {
        let text = String::from("hello");
        result = &text;  // E0597: text does not live long enough
    }  // text dropped here
    println!("{}", result);  // dangling reference!
    
    // Fix A — move text to outer scope
    let text = String::from("hello");
    let result = &text;  // text lives as long as result
    println!("{}", result);
    
    // Fix B — own the data
    let result: String;
    {
        result = String::from("hello");  // move, not reference
    }
  4. 04

    step 4

    Use 'static for string literals and truly static data

    `&'static str` references data that lives for the entire program — string literals embedded in the binary. Use `'static` only for genuinely static data, not as a workaround for lifetime complexity.

    // String literals are 'static
    let s: &'static str = "I live for the whole program";
    
    // A function returning 'static only works if the returned reference is to static data
    fn error_message(code: u32) -> &'static str {
        match code {
            404 => "Not found",
            500 => "Internal error",
            _ => "Unknown error",  // all are &'static str literals
        }
    }

How to verify the fix

  • `cargo build` passes without lifetime errors.
  • No unnecessary `'static` bounds used as a workaround — `'static` should only apply to genuinely static data.
  • `cargo clippy` shows no lifetime-related warnings.

Why E0106 / E0597 / E0623 happens at the runtime level

Rust's lifetime system is a region-based type system layered on top of the borrow checker. Each reference has a lifetime — a region of code during which it's valid. The compiler infers lifetimes for most cases via elision rules, but when the inference is ambiguous (a function with multiple input references and one output reference, or a struct with stored references), explicit annotations are required to resolve the ambiguity. Lifetime annotations are checked during the borrow checking phase: the compiler verifies that no reference can ever be used after the data it points to has been dropped. This is guaranteed statically — no runtime checks, no garbage collector.

Common debug mistakes for E0106 / E0597 / E0623

  • Returning a reference to a local variable — the local is dropped when the function returns, making the reference dangling.
  • Using `'static` as a quick fix when the actual issue is a lifetime annotation on a struct — `'static` is correct only for globally-alive data.
  • Mixing up lifetime parameter syntax on `impl` blocks — `impl<'a> MyStruct<'a>` requires both the `impl<'a>` and the `<'a>` on the type.
  • Thread spawning closures that capture references — threads require `'static` because the thread may outlive the scope where the reference was created.
  • Async functions with references in async blocks — async state machines capture the reference and must hold it across `await` points, requiring explicit lifetime bounds.

When E0106 / E0597 / E0623 signals a deeper problem

Complex lifetime annotations in application code (as opposed to library code) usually signal that the data ownership model needs simplification. Application code that requires 3-level lifetime annotations (`'a: 'b + 'c`) to compile is typically better expressed with owned types and `Arc` for shared ownership. Libraries need explicit lifetime annotations for zero-copy APIs (parsers, serializers). Applications should prefer clarity over zero-copy optimization unless profiling identifies the allocation as a bottleneck. The Rust idiom is: start with `String`, `Vec<T>`, `Arc<T>`; optimize with references and lifetimes after profiling.

Editor's take

Lifetime errors are the Rust learning curve's steepest section. The correct mental model takes weeks to build, and without it, the errors look arbitrary. The key insight: lifetimes are not about when things are created — they're about how long references remain valid. A lifetime annotation is a contract: 'this output reference will be valid for at least as long as this input reference.' The compiler checks that every call site honors the contract.

The practical resolution path that gets you unstuck fastest: if the lifetime error is on a struct field, change the field from `&str` to `String`, or from `&[T]` to `Vec<T>`. You pay one allocation but eliminate the lifetime parameter from the struct entirely. This makes every `impl` block simpler, every function using the struct simpler, and every caller simpler. Profile before deciding whether to optimize with references.

The async Rust version of lifetime errors is particularly painful. When you write an async function that takes a `&str` parameter and awaits inside, the compiler needs to prove the reference is valid across the await point — because the async state machine is a struct that captures all local variables, and it may be held across executor scheduling boundaries. This is why `tokio::spawn(async move { ... })` uses `move` to take ownership — capturing references in a spawned task is almost always wrong because the task may outlive the referent. The idiomatic fix is to clone the data before moving it into the async block, or use `Arc<T>` for shared ownership across tasks.

By Bikram Nath · Curator · Updated June 2026

Frequently asked questions

What are lifetime elision rules — why don't most functions need annotations?

Rust automatically infers lifetimes in common patterns via elision rules: (1) each reference parameter gets its own lifetime, (2) if there's exactly one input lifetime, it's assigned to all output lifetimes, (3) if one of the inputs is `&self` or `&mut self`, its lifetime is assigned to all output lifetimes. Most function signatures match one of these patterns. When they don't, explicit annotations are required.

Should I just clone() everything to avoid lifetime annotations?

Cloning to avoid lifetime annotations is a valid pragmatic choice — especially early in a project or when the data is small. It trades memory safety annotation complexity for a small runtime allocation. As the codebase matures, hot paths can be refactored to use references with lifetimes for performance. Don't over-optimize prematurely. `String` instead of `&str`, `Vec<T>` instead of `&[T]`, and `PathBuf` instead of `&Path` are idiomatic choices that avoid lifetime complexity at the cost of a heap allocation.

What does the + 'static bound in trait objects mean?

`Box<dyn Trait + 'static>` means the trait object cannot hold any references that outlive 'static — in practice, it means the concrete type behind the trait object must own all its data (no non-static references inside it). This is required by `tokio::spawn` and thread boundaries where the spawned future must live independently. The fix: own the data or use `Arc<T>` instead of `&T` inside the trait implementor.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is cross-referenced against the official sources listed in the “sources” sidebar before it ships. If a fix here didn’t work for you, please email so we can update the page.