Rust Error E0597: Borrowed Value Does Not Live Long Enough
E0597 means: you kept a reference (&x) around longer than the value x itself. The value gets cleaned up at the end of its block, but something outside that block still wants to use the reference. It’s a very common error, especially with inner blocks, loops and structs, and the fix is usually to move one line or to store an owned value instead of a reference. (This page is part of Rust compiler errors explained.)
The error message
Section titled “The error message”error[E0597]: `x` does not live long enough --> src/main.rs:5:13 |4 | let x = 5; | - binding `x` declared here5 | r = &x; // r borrows x | ^^ borrowed value does not live long enough6 | } // x is dropped here | - `x` dropped here while still borrowed7 | println!("r: {r}"); // ❌ r would point at nothing | - borrow later used hereThis is the program that produces it:
fn main() { let r; { let x = 5; r = &x; // r borrows x } // x is dropped here println!("r: {r}"); // ❌ r would point at nothing}What it means
Section titled “What it means”The four labels tell the story in order:
- Line 4:
xis created inside the inner block{ ... }. - Line 5:
rborrowsx. That’s fine so far. - Line 6: the block ends, so
xis dropped (cleaned up). The borrow is still alive. - Line 7:
ris used, but the value it points to is already gone.
Think of writing down a friend’s hotel room number. That’s useful while they’re staying there. Once they check out, the number still exists on your note, but it now leads to an empty room, or a stranger’s. Rust won’t let you keep a note that leads to an empty room.
You’ll also see the short label “borrowed value does not live long enough”. It’s the same error; the headline just names the variable.
Why Rust does this
Section titled “Why Rust does this”A reference must never outlive the value it points to. If it did, you’d have a dangling reference: a pointer to memory that has been freed and may now hold something else. In C and C++ that’s a classic source of crashes and security bugs (“use after free”).
Every value in Rust is dropped when its owner goes out of scope, usually at the closing } of the block where it was declared. The borrow checker compares two spans: how long the value lives and how long the reference is used. If the reference is used after the value’s }, you get E0597. See Scope, Borrowing and Lifetimes.
Notice what the compiler does not complain about: borrowing x on line 5. The problem is only the later use on line 7. Remove that last line and the program compiles. That’s a useful hint for the fixes below: either make the value live longer, or make the reference’s use end sooner.
How to fix it
Section titled “How to fix it”Fix 1: Move the value to an outer scope
Section titled “Fix 1: Move the value to an outer scope”Choose this when the value really is needed for as long as the reference. This is the most common fix: declare the value before (outside) the block, so it lives at least as long as the reference.
fn main() { let scores = vec![72, 95, 88]; // now lives until the end of main let best; { best = scores.iter().max().unwrap(); } println!("Best score: {best}"); // ✅ scores is still alive}Output:
Best score: 95Rule of thumb: the owner must be declared before the borrower and outlive it. If you see a value created inside a block or a loop and a reference to it used outside, move the value out.
Fix 2: Store owned values instead of references
Section titled “Fix 2: Store owned values instead of references”Choose this when you collect values created inside a loop. Each pass of the loop creates a new String and drops it at the end of the pass, so a Vec<&str> outside the loop can’t keep references to them:
fn main() { let mut names: Vec<&str> = Vec::new(); for i in 1..=3 { let name = format!("player{i}"); names.push(&name); // ❌ `name` does not live long enough } println!("{names:?}");}The fix is to let the Vec own the strings. Change Vec<&str> to Vec<String> and push the String itself (this moves it into the vector, so it lives as long as the vector does):
fn main() { let mut names: Vec<String> = Vec::new(); for i in 1..=3 { let name = format!("player{i}"); names.push(name); // move the String into the Vec } println!("{names:?}");}Output:
["player1", "player2", "player3"]The same idea applies to structs and HashMaps: if the data is created in a smaller scope than the container, store String (or Vec<T>) instead of &str (or &[T]). Owned fields are almost always the right choice for beginners. See Strings and Vectors.
Fix 3: Clone it
Section titled “Fix 3: Clone it”Choose this when you need to keep using the original too, or you only have a borrow of it. A struct that holds a reference (Label<'a> { text: &'a str }) can’t outlive the string it points to. Give the struct an owned String field and store a copy:
struct Label { text: String, // owned, not &str}
fn main() { let label; { let title = String::from("Inbox"); label = Label { text: title.clone() }; println!("Inside the block: {title}"); } println!("Label: {}", label.text); // ✅ label owns its own copy}Output:
Inside the block: InboxLabel: InboxIf you don’t need title afterwards, skip the clone and move it: Label { text: title }. Cloning a short string is cheap and perfectly fine. If you only have a &str, use .to_string() to get an owned copy.
Fix 4: Restructure so the borrow ends in time
Section titled “Fix 4: Restructure so the borrow ends in time”Choose this when the value is meant to be short-lived. Instead of making the value live longer, use the reference while the value is still alive. This often comes up with functions that return a reference, like the classic longest example:
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn main() { let first = String::from("Ferris the crab"); { let second = String::from("Corro"); let winner = longest(&first, &second); println!("Longest name: {winner}"); // ✅ used before second is dropped }}Output:
Longest name: Ferris the crabIf winner is declared outside the block and printed after it, you get E0597 for second, even though first happens to be longer. The signature says the result may borrow from either argument, so both must outlive it. If you really need the result later, store it owned: let winner = longest(&first, &second).to_string();.
Where you’ll usually see it
Section titled “Where you’ll usually see it”A closure stored outside the block. A closure that uses name borrows it. If the closure outlives name, you get E0597 with an extra label, value captured here:
let say_hello;{ let name = String::from("Ferris"); say_hello = || println!("Hello, {name}!"); // ❌ `name` does not live long enough}say_hello();Fix: write move || ... so the closure takes ownership of name. See Closures.
Threads. Borrowing a local inside thread::spawn(|| ...) gives a different error, E0373 (“closure may outlive the current function”), because the thread could run longer than main. Fix: thread::spawn(move || ...), share with Arc, or use std::thread::scope. See Concurrency.
A reference to a temporary. let label = Label { text: String::from("Inbox").as_str() }; is close to E0597 but shows up as E0716, “temporary value dropped while borrowed”: the String is never stored in a variable, so it’s dropped at the end of the statement. Fix: put it in a variable first (let title = String::from("Inbox");), or make the field a String.
Returning a reference from a function. If the value is created inside the function, you get E0515 instead. Return the owned value.
Related errors
Section titled “Related errors”- E0515: cannot return reference to local variable: the same problem across a function boundary.
- E0373: closure may outlive the current function: the thread version of this error. Usually fixed with
move. - E0106: missing lifetime specifier: Rust needs to know what a returned or stored reference borrows from.
- E0382: borrow of moved value: the opposite trap, using a value after you gave it away.
- E0502: mutable borrow while immutably borrowed: a reference is still in use when you try to change the value.
E0716: temporary value dropped while borrowed: like E0597, but for a value that was never stored in a variable.- All compiler errors explained
Quick check
Section titled “Quick check”Quick check
1. In let r; { let x = 5; r = &x; } followed by a println! of r, what actually triggers E0597?
2. You build a String in each loop pass and push &name into a Vec<&str>. What's the usual fix?
3. A thread closure borrows a local Vec. Which error do you get?
Score: 0 / 3
Read more
Section titled “Read more”- E0597 — Rust error codes index: the official explanation of this error.
- The Borrow Checker — The Rust Book: the same
randxexample, with a diagram of the two lifetimes. - Borrowing — Rust by Example: short runnable examples of borrows and how long they last.
- Lifetimes — Rust by Example: how the compiler sees the life of each value and each borrow.
- E0716 — Rust error codes index: the closely related “temporary value dropped while borrowed” error.