Rust errors / Ownership and borrowing
Rust compiler error · checked with rustc 1.99.0
Rust error E0373: closure may outlive the current function, but it borrows
What it meansA closure that may run after the current function returns (a thread, an async task, a returned closure) borrows a local variable. Make it take ownership with move.
What rustc prints
error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function --> main.rs:3:14 | 3 | Box::new(|y| x + y) | ^^^ - `x` is borrowed here | | | may outlive borrowed value `x` | note: closure is returned here --> main.rs:3:5 | 3 | Box::new(|y| x + y) | ^^^^^^^^^^^^^^^^^^^ help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword | 3 | Box::new(move |y| x + y) | ++++
Real output of rustc 1.99.0 compiling the official example below (2021 edition). Your names and line numbers will differ; the first line is the part to match.
Why it happens
thread::spawn(|| println!("{}", x)).tokio::spawn(async { use(&data) }).- Returning
impl Fnthat captures a local by reference.
How to fix it
1. Add move
The closure takes its own copy or ownership.
let name = name.clone();
std::thread::spawn(move || println!("{name}"));2. Share with Arc
When the original is still needed.
let data = Arc::new(data);
let d = Arc::clone(&data);
tokio::spawn(async move { use_data(&d).await });Other wordings of this error
closure may outlive the current function, but it borrows, to force the closure to take ownership, use the `move` keyword
The compiler team's explanation
A captured variable in a closure may not live long enough.
Erroneous code example:
fn foo() -> Box<dyn Fn(u32) -> u32> {
let x = 0u32;
Box::new(|y| x + y)
}
This error occurs when an attempt is made to use data captured by a closure, when that data may no longer exist. It's most commonly seen when attempting to return a closure as shown in the previous code example.
Notice that x is stack-allocated by foo(). By default, Rust captures closed-over data by reference. This means that once foo() returns, x no longer exists. An attempt to access x within the closure would thus be unsafe.
Another situation where this might be encountered is when spawning threads:
fn foo() {
let x = 0u32;
let y = 1u32;
let thr = std::thread::spawn(|| {
x + y
});
}
Since our new thread runs in parallel, the stack frame containing x and y may well have disappeared by the time we try to use them. Even if we call thr.join() within foo (which blocks until thr has completed, ensuring the stack frame won't disappear), we will not succeed: the compiler cannot prove that this behavior is safe, and so won't let us do it.
The solution to this problem is usually to switch to using a move closure. This approach moves (or copies, where possible) data into the closure, rather than taking references to it. For example:
fn foo() -> Box<dyn Fn(u32) -> u32> {
let x = 0u32;
Box::new(move |y| x + y)
}
Now that the closure has its own copy of the data, there's no need to worry about safety.
This error may also be encountered while using async blocks:
use std::future::Future;
async fn f() {
let v = vec![1, 2, 3i32];
spawn(async { //~ ERROR E0373
println!("{:?}", v)
});
}
fn spawn<F: Future + Send + 'static>(future: F) {
unimplemented!()
}
Similarly to closures, async blocks are not executed immediately and may capture closed-over data by reference. For more information, see <https://rust-lang.github.io/async-book/03_async_await/01_chapter.html>.
From rustc --explain E0373 (1.99.0), part of the Rust project, MIT or Apache-2.0. Official page.
Related errors
- E0382borrow of moved value
- E0499cannot borrow as mutable more than once at a time
- E0502cannot borrow as mutable because it is also borrowed as immutable
- E0106missing lifetime specifier
- E0311the parameter type `T` may not live long enough
- E0316nested quantification of lifetimes
- E0384cannot assign twice to immutable variable
Up next
Often hit nextE0382: borrow of moved valueA related error from the same family.Read it next →Did this answer your question?