Closures

Capture by Value

A closure captures a variable by value (T) when the closure body consumes ownership of that variable—such as passing it to a function by value, returning it from the closure, calling a self-consuming method like .into_iter(), or when the closure is declared with move.

Automatic By-Value Capture When Ownership Is Consumed

Even without the move keyword, the compiler automatically moves a non-Copy variable into the closure when the closure body transfers ownership out of that variable:

fn main() {
    let payload = vec![String::from("chunk-1"), String::from("chunk-2")];

    // `.into_iter()` takes `self` by value, forcing `payload` to be moved into `consume`:
    let consume = || {
        for part in payload.into_iter() {
            println!("Sending {part}");
        }
    };

    // `payload` has already been moved into `consume`; calling `consume()` runs it once:
    consume();
}
Sending chunk-1
Sending chunk-2

Use-After-Move Error in the Enclosing Scope (E0382)

Once a non-Copy variable is captured by value, ownership transfers into the closure at the point where the closure is defined, not when it is called. Accessing the variable in the outer scope—or calling a consuming closure a second time—fails with E0382:

// ❌ does not compile
fn main() {
    let token = String::from("secret-jwt");

    let take_token = || {
        let owned = token; // Moves `token` out of the closure when called
        println!("Taken: {owned}");
    };

    println!("Still here? {token}"); // Error: `token` was moved into `take_token`
    take_token();
}
error[E0382]: borrow of moved value: `token`
  --> src/main.rs:10:27
   |
3  |     let token = String::from("secret-jwt");
   |         ----- move occurs because `token` has type `String`, which does not implement the `Copy` trait
4  |
5  |     let take_token = || {
   |                      -- value moved into closure here
6  |         let owned = token;
   |                     ----- variable moved due to use in closure
...
10 |     println!("Still here? {token}");
   |                           ^^^^^^^ value borrowed here after move

By-Value Capture of Copy vs. Non-Copy Types

When a variable is captured by value, its behavior depends on whether its type implements Copy:

Captured Type Effect on Outer Binding Can the Closure Be Called Multiple Times?
Copy type (i32, bool, &T) Copied into the closure; outer binding remains valid Yes (Fn / FnMut + FnOnce)
Non-Copy type (String, Vec<T>) moved into closure but only read/mutated inside Moved into the closure; outer binding is invalidated Yes (Fn or FnMut + FnOnce)
Non-Copy type moved out of the closure body on call Moved into the closure and consumed on the first call No — can only be called once (FnOnce only)