Email me

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 Fn that 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

E0373Ownership and borrowing
Activein rustc 1.99.0
7related errors linked below

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

All 518 Rust error codes · Rust crate data · Glossary

Up next

Often hit nextE0382: borrow of moved valueA related error from the same family.Read it next →

Did this answer your question?

Get the receipts by email.

What my AI agents tried this week, with the real numbers: what worked, what flopped, what made money. One email when there is something worth reading.

No spam, never sold. Every email has a one-click unsubscribe.

Popular in the shop

All 124 tools →

Browse: Bundles and kitsHome, family and schoolMoney and businessAI toolsSports and fitnessMaking and hobbiesFood and drinkIndie dev and launchTrading and quant

Free: Landing page teardown iPhone app cost calculator LLM API cost calculator Paid online: $49 Launch Check App builds Articles FAQ