Rust Error E0716: Temporary Value Dropped While Borrowed
E0716 means: you borrowed from a value that has no variable name, and then kept using that borrow on a later line. Rust throws such nameless values away at the end of the statement, so the borrow would point at nothing. It looks scary, but it’s one of the easiest borrow errors to fix: usually you just split one line into two. (This page is part of Rust compiler errors explained.)
The error message
Section titled “The error message”error[E0716]: temporary value dropped while borrowed --> src/main.rs:2:13 |2 | let s = String::from("hi").as_str(); // s borrows a String that has no name | ^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary value which is freed while still in use3 | println!("{s}"); // ❌ that String is already gone | - borrow later used here |help: consider using a `let` binding to create a longer lived value |2 ~ let binding = String::from("hi");3 ~ let s = binding.as_str(); // s borrows a String that has no name |This is the program that produces it:
fn main() { let s = String::from("hi").as_str(); // s borrows a String that has no name println!("{s}"); // ❌ that String is already gone}What it means
Section titled “What it means”A temporary is a value that is created in the middle of an expression but never stored in a variable. In the line above, String::from("hi") builds a real String, but nothing owns it: there is no let name = ... for it. Rust drops (cleans up) a temporary at the end of the statement, which is the ;.
Read the three labels from left to right:
^^^^underString::from("hi"): here a temporaryStringis created.-at the end of the line: here, at the;, thatStringis freed.-underson line 3: buts(a&strpointing into thatString) is still used here.
Think of a waiter who writes your order on a sticky note and throws the note away as soon as they walk off. If you tell a friend “the order is on that note”, your friend finds nothing. You have to keep the note yourself, or write the order down somewhere that stays.
The help: part of the message is usually the answer: create a variable (rustc calls it binding) so the value has an owner that lives longer.
Why Rust does this
Section titled “Why Rust does this”This is the same rule as E0597: a reference must never outlive the value it points to, or it becomes a dangling reference to freed memory. The only difference is that in E0597 the value has a name and dies at a }, while in E0716 the value has no name and dies at the ;.
Using a temporary inside the same statement is completely fine. println!("{}", String::from("hi").as_str()); compiles, because the borrow ends before the ;. The error only appears when the borrow is still needed on a later line. See When does a borrow end? and Lifetimes.
There is one helpful exception, called temporary lifetime extension. If you put & (or &mut) directly in front of the temporary in a let, Rust quietly keeps the temporary alive as long as the variable. That’s why these lines compile:
struct Label<'a> { text: &'a str,}
fn make_title() -> String { String::from("Inbox")}
fn main() { let greeting = &String::from("hi"); // ✅ extended: & right in the let let queue = &mut vec![1, 2, 3]; // ✅ extended queue.push(4); let label = Label { text: &make_title() }; // ✅ extended: & directly in a struct literal println!("{greeting} {queue:?} {}", label.text);}Output:
hi [1, 2, 3, 4] InboxThe extension does not pass through a method call or a function call. String::from("hi").as_str(), make_title().as_str(), vec![1, 2].iter() and shout(&make_title()) all borrow the temporary inside a call, so the temporary still dies at the ; and you get E0716. Don’t try to memorize every case: if you see E0716, give the value a name.
How to fix it
Section titled “How to fix it”Fix 1: Give the temporary a name with let
Section titled “Fix 1: Give the temporary a name with let”Choose this almost every time. It’s what the compiler suggests, and it’s the right answer for most E0716 errors. Store the value in its own variable first, then borrow from that variable on the next line.
A very common case is splitting a line of input. read_command() returns a new String, .trim() and .split(' ') borrow from it, and the Vec<&str> keeps those borrows:
let parts: Vec<&str> = read_command().trim().split(' ').collect(); // ❌ E0716Rust points at read_command() and says it is freed at the end of the statement. Give it a name:
fn read_command() -> String { // In a real program this would read a line from stdin. String::from(" add milk eggs \n")}
fn main() { let line = read_command(); // the String now lives until the end of main let parts: Vec<&str> = line.trim().split(' ').collect(); println!("{parts:?}"); println!("command: {}, items: {}", parts[0], parts.len() - 1);}Output:
["add", "milk", "eggs"]command: add, items: 2You’ll hit exactly this with real input too: lines.next().unwrap().unwrap().trim().split(' ').collect() on io::stdin().lines() gives E0716 for the same reason. Put let line = lines.next().unwrap().unwrap(); on its own line first. See Strings.
Fix 2: Keep owned values instead of references
Section titled “Fix 2: Keep owned values instead of references”Choose this when the borrow needs to live longer than any variable you could create, for example in a struct you pass around, or in a Vec you fill inside a loop. A let inside a loop would be dropped at the end of each pass, which only turns E0716 into E0597.
Here are two broken versions. A struct holding a &str from a temporary, and a loop that pushes borrows of new strings:
let label = Label { text: make_title().as_str() }; // ❌ E0716
let mut names: Vec<&str> = Vec::new();for name in ["ferris", "corro"] { names.push(name.to_uppercase().as_str()); // ❌ E0716}The fix is to let the struct and the Vec own their strings. Use String instead of &str, and drop the .as_str():
struct Label { text: String, // owned, so no lifetime needed}
fn make_title() -> String { String::from("Inbox")}
fn main() { let label = Label { text: make_title() }; // the String moves into the Label println!("Label: {}", label.text);
let mut names: Vec<String> = Vec::new(); for name in ["ferris", "corro"] { names.push(name.to_uppercase()); // the Vec owns each new String } println!("{names:?}");}Output:
Label: Inbox["FERRIS", "CORRO"]For beginners, owned fields (String, Vec<T>) are almost always the easiest choice. A struct with a &str field needs a lifetime and can never outlive the data it borrows. See Structs that hold references and Structs.
Fix 3: Restructure the chain
Section titled “Fix 3: Restructure the chain”Choose this when the temporary is the start of an iterator chain. These lines create a Vec with vec![...], borrow it with .iter(), and keep the iterator (or its result) for later:
let doubled = &mut vec![1, 2, 3].iter().map(|n| n * 10); // ❌ E0716let best = vec![72, 95, 88].iter().max(); // ❌ E0716The &mut in front doesn’t help: lifetime extension keeps the iterator alive, but not the Vec that .iter() borrows from. Two ways out:
- Use
into_iter()so the iterator owns theVec(rustc suggests this too). Then you don’t need&muteither, justlet mut. - Finish the chain with an owned result, like
.copied(),.cloned(),.sum()or.collect::<Vec<_>>()of plain numbers, so nothing borrows the temporary after the;.
fn main() { // The iterator owns the Vec, so nothing borrows a temporary let mut doubled = vec![1, 2, 3].into_iter().map(|n| n * 10); println!("{:?}", doubled.next()); println!("{:?}", doubled.next());
// .copied() turns Option<&i32> into Option<i32>, a value with no borrow let best = vec![72, 95, 88].iter().max().copied(); println!("Best: {best:?}");}Output:
Some(10)Some(20)Best: Some(95)If you need the Vec again later, go back to Fix 1: let scores = vec![72, 95, 88]; and then scores.iter().max(). See Iterators.
Where you’ll usually see it
Section titled “Where you’ll usually see it”Locking a Mutex in the middle of a chain. let first = names.lock().unwrap().first(); gives E0716, because the lock guard is a temporary. When it’s dropped, the lock is released, so first can’t keep pointing inside. Fix: let guard = names.lock().unwrap(); then guard.first(), or copy the data out in one go with .first().cloned(). See Shared State.
Passing &temporary to a function that returns a reference. With fn shout(s: &str) -> &str, the line let t = shout(&make_title()); gives E0716: the returned &str borrows the temporary String, and lifetime extension doesn’t reach through a function call. Fix: let title = make_title(); then shout(&title).
Returning it from a function. If you write make_title().as_str() as the last line of a function that returns a reference (like -> &'static str), you get E0515 (“cannot return value referencing temporary value”) instead. Fix: return the String.
Related errors
Section titled “Related errors”- E0597: borrowed value does not live long enough: the same problem for a value that has a name and is dropped at a
}. - E0515: cannot return reference to local variable: returning a borrow of a local or temporary value from a function.
- E0106: missing lifetime specifier: a struct field like
text: &strneeds a lifetime. Often a sign thatStringis the better choice. - E0502: mutable borrow while immutably borrowed: another “a borrow is still in use” error, about changing data instead of dropping it.
- E0382: borrow of moved value: after switching to owned values, watch out for using a value after you moved it.
- All compiler errors explained
Quick check
Section titled “Quick check”Quick check
1. In Rust, what is a "temporary"?
2. let s = String::from("hi").as_str(); followed by println!("{s}"); gives E0716. What is the usual fix?
3. Which line compiles, thanks to temporary lifetime extension?
Score: 0 / 3
Read more
Section titled “Read more”- E0716 — Rust error codes index: the official explanation of this error, with the
letfix. - References and Borrowing — The Rust Book: the borrowing rules, including why references can never dangle.
- Temporary scopes — The Rust Reference: the exact rules for when a temporary is dropped.
- Temporary lifetime extension — The Rust Reference: which
letforms keep a temporary alive, with examples. - Validating References with Lifetimes — The Rust Book: how Rust compares how long a value lives with how long a reference is used.