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]