Lifetimes

Common Lifetime Errors

Lifetime compiler errors occur when a reference's signature omits a required lifetime annotation or when a borrow's actual scope cannot satisfy the lifetime relationships declared in a function or struct.

Summary of Common Lifetime Error Codes

Error Code Message Summary Root Cause Typical Fix
E0106 missing lifetime specifier Returning a reference or defining a struct reference field where elision cannot infer a single source lifetime Add <'a> and annotate the matching input parameter(s) or struct field
E0515 cannot return value referencing local variable Returning &local where local is owned by the current function frame Return an owned value (String, Vec<T>) instead of a reference
E0597 `x` does not live long enough Value x is dropped while a reference derived from x is still live or constrained to a longer lifetime Broaden x's scope, decouple independent lifetimes ('a, 'b), or clone the data
E0621 explicit lifetime required in the type of ... A function returns data from an unannotated parameter whose lifetime was not linked to the output Annotate the second parameter with the output lifetime 'a
E0716 temporary value dropped while borrowed Taking a reference to an unstored temporary expression (e.g., &String::from("hi").to_uppercase()) that is dropped at the semicolon Bind the temporary to a let variable first so its lifetime extends to the block

Error 1: Temporary Value Dropped While Borrowed (E0716)

When you chain a method that produces a temporary owned value (such as to_lowercase() returning a String) and immediately take a slice of it (.as_str() or .trim()), the temporary String is dropped at the end of the let statement:

// ❌ does not compile
fn main() {
    let trimmed: &str = String::from("  HELLO  ").to_lowercase().trim();
    println!("{trimmed}");
}
error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:3:25
  |
3 |     let trimmed: &str = String::from("  HELLO  ").to_lowercase().trim();
  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^       - temporary value is freed at the end of this statement
  |                         |
  |                         creates a temporary value which is freed while still in use
4 |     println!("{trimmed}");
  |               --------- borrow later used here
  |
help: consider using a `let` binding to create a longer lived value
  |
3 ~     let binding = String::from("  HELLO  ").to_lowercase();
4 ~     let trimmed: &str = binding.trim();
  |

Fix: Bind the Intermediate Owned Value

fn main() {
    let lowered = String::from("  HELLO  ").to_lowercase();
    let trimmed: &str = lowered.trim();
    println!("{trimmed}");
}
hello

Error 2: Over-Constrained Shared Lifetimes (E0597)

Using the same lifetime 'a on a mutable borrow (&'a mut Self) and on the struct's lifetime (Struct<'a>) locks the struct exclusively for the entire remaining lifetime 'a:

// ❌ does not compile
struct Buffer<'a> {
    data: &'a mut [u8],
}

impl<'a> Buffer<'a> {
    // Anti-pattern: `&'a mut self` borrows `self` mutably for ALL of `'a`!
    fn reset(&'a mut self) {
        self.data.fill(0);
    }
}

fn main() {
    let mut raw = [1, 2, 3];
    let mut buf = Buffer { data: &mut raw };
    buf.reset();
    buf.reset(); // Error: `buf` is still mutably borrowed from the first call!
}
error[E0499]: cannot borrow `buf` as mutable more than once at a time
  --> src/main.rs:17:5
   |
16 |     buf.reset();
   |     --- first mutable borrow occurs here
17 |     buf.reset(); // Error: `buf` is still mutably borrowed from the first call!
   |     ^^^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

Fix: Never Write &'a mut self on impl<'a> Type<'a> Unless Intended

Let &mut self have its own short, elided borrow lifetime (fn reset(&mut self)) rather than tying the borrow of self to 'a:

struct Buffer<'a> {
    data: &'a mut [u8],
}

impl<'a> Buffer<'a> {
    fn reset(&mut self) {
        self.data.fill(0);
    }
}

fn main() {
    let mut raw = [1, 2, 3];
    let mut buf = Buffer { data: &mut raw };
    buf.reset();
    buf.reset();
    println!("After two resets: {:?}", buf.data);
}
After two resets: [0, 0, 0]