Rust error E0597: borrowed value does not live long enough
Why Rust says a borrowed value does not live long enough (E0597), with fixes for inner scopes, loops that store references, and returning references.
E0597 means a reference would still be used after the value it points to has been dropped. If the program were allowed to run, the reference would point at freed memory (a dangling reference). The fix is always one of: make the value live longer, make the reference live shorter, or store an owned value instead of a reference.
The basic case: an inner scope
fn main() {
let longest: &str;
{
let name = String::from("Ferris");
longest = &name;
}
println!("{longest}");
}
error[E0597]: `name` does not live long enough
--> src/main.rs:5:19
|
4 | let name = String::from("Ferris");
| ---- binding `name` declared here
5 | longest = &name;
| ^^^^^ borrowed value does not live long enough
6 | }
| - `name` dropped here while still borrowed
7 | println!("{longest}");
| ------- borrow later used here
The labels show the timeline: where the value is declared, where it's borrowed, where it's dropped (the closing brace), and where the borrow is used after that. Fixes: declare name in the outer scope, or make longest an owned String (longest = name; moves it out).
Storing references to temporaries in a loop
fn main() {
let mut labels: Vec<&str> = Vec::new();
for i in 0..3 {
let label = format!("item-{i}");
labels.push(&label);
}
println!("{labels:?}");
}
error[E0597]: `label` does not live long enough
Each label is dropped at the end of its loop iteration, but the vector keeps a reference to it. rustc notes "labels is a collection that stores borrowed references, but label does not live long enough to be stored in it". The fix is to let the vector own the strings: Vec<String> and labels.push(label).
This is the most common E0597 at work: a struct or collection typed with references (&str, &T) that is filled with values created on the fly. When the data is created inside the loop or function, the container must own it.
Returning a reference to a local
Returning &s where s is a local String gets its own error code, E0515 ("cannot return reference to local variable s"), and a signature like fn make() -> &str with no reference parameters gets E0106. Same idea, same fix: return the owned String.
Threads and 'static
Passing a reference to a local into something that needs 'static data, like thread::spawn or tokio::spawn, reports E0597 with "argument requires that x is borrowed for 'static" (or E0373 for closures). Move owned data into the thread with move, share it with Arc, or use thread::scope so the threads end before the data does.
How to think about it
Ask: who owns this data, and is that owner still alive when I use the reference? References are for temporary access to data someone else owns for longer. If nobody owns it for long enough, own it yourself.
Example
use std::thread;
fn make_greeting(name: &str) -> String {
format!("Hello, {name}")
}
fn main() {
// Fix 1: the owner lives in the outer scope.
let name = String::from("Ferris");
let longest: &str;
{
longest = &name;
}
println!("{longest}");
// Fix 2: the collection owns its strings.
let mut labels: Vec<String> = Vec::new();
for i in 0..3 {
let label = format!("item-{i}");
labels.push(label);
}
println!("{labels:?}");
// Fix 3: return owned data from functions.
println!("{}", make_greeting("Corro"));
// Fix 4: scoped threads may borrow, because they end before the data does.
let totals = vec![3, 4, 5];
let sum = thread::scope(|s| s.spawn(|| totals.iter().sum::<i32>()).join().unwrap());
println!("sum {sum} of {totals:?}");
}
Output:
Ferris
["item-0", "item-1", "item-2"]
Hello, Corro
sum 12 of [3, 4, 5]