Skip to content

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.)

error[E0597]: `x` does not live long enough
--> src/main.rs:5:13
|
4 | let x = 5;
| - binding `x` declared here
5 | r = &x; // r borrows x
| ^^ borrowed value does not live long enough
6 | } // x is dropped here
| - `x` dropped here while still borrowed
7 | println!("r: {r}"); // ❌ r would point at nothing
| - borrow later used here

This 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
}

The four labels tell the story in order:

  1. Line 4: x is created inside the inner block { ... }.
  2. Line 5: r borrows x. That’s fine so far.
  3. Line 6: the block ends, so x is dropped (cleaned up). The borrow is still alive.
  4. Line 7: r is 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.

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.

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: 95
▶ Try it Yourself

Rule 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"]
▶ Try it Yourself

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.

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: Inbox
Label: Inbox
▶ Try it Yourself

If 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 crab
▶ Try it Yourself

If 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();.

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.

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