🎓 A1Learner
🔍

Rust Closure Captures

A closure capture is how a closure holds a variable from around it. A closure may capture by shared borrow, by &mut borrow, or by value.

A closure remembers what it uses

This closure reads word from the code around it — it needs no parameter for it:

let word = String::from("rust");
let shout = || word.to_uppercase();

The method you call picks the capture

Watch one String meet three closures. Reading it captures a shared borrow. Changing it captures &mut. Hand it to a method that takes it by value. Then the closure takes the String itself. The receiver of the method call decides:

let mut word = String::from("rust");
let read = || word.len();
println!("len = {}", read());
let mut grow = || word.push('!');
grow();
println!("now = {word}");
let consume = || word.into_bytes();
println!("bytes = {}", consume().len());

The first call locks the types

A closure's types are fixed by its first call. The second call below mixes types, so the compiler refuses it:

let id = |x| x;
println!("{}", id(String::from("a")));
println!("{}", id(3)); // error E0308: mismatched types

Dropping a capture makes it FnOnce

This closure hands its String to drop. So the String is gone after one call. The closure itself can only be called once:

let word = String::from("rust");
let eat = move || drop(word);
eat();

move copies a Copy value

move forces ownership — but a value whose type is Copy is copied into the closure instead of moved. The original stays usable afterwards:

let limit = 3;
let check = move |n: i32| n <= limit;
println!("{} {}", check(2), limit);

Borrows end at the last call

Once grow has been called for the last time, its &mut borrow is over — word itself can be read again:

let mut word = String::from("rust");
let mut grow = || word.push('!');
grow();
println!("{word}");

Example

fn main() {
    let list = vec![1, 2, 3];
    let only_borrows = || println!("From closure: {list:?}");
    println!("Before calling closure: {list:?}");
    only_borrows();
    let mut list2 = vec![1, 2, 3];
    let mut borrows_mutably = || list2.push(7);
    borrows_mutably();
    println!("After calling closure: {list2:?}");
    let owned = String::from("hello");
    let take = move || println!("Moved in: {owned}");
    take();
}

Key points

  • Type annotations are allowed but rarely needed: |x: i32| -> i32 { x + 1 }
  • A closure may hold a &mut borrow.
  • While it does, no other borrow is allowed.
  • That ban runs from defining the closure to calling it.
  • It covers borrows of that value
  • Use move before the bars to force ownership.
  • Do this even when the body only reads.
  • Threads need this.
  • The thread may outlive the scope
  • Capture strength climbs in a fixed order: shared borrow, then a unique borrow, then &mut borrow, then by value.
  • When one place is used in two ways, the stronger way wins
  • Writing through a captured &mut reference is special.
  • The code *r = 5 does this.
  • It borrows the reference itself in a unique way.
  • While that closure lives, nobody else may borrow the reference.
  • Nobody may borrow it at all (E0501)
  • The body decides which capture each variable gets.
  • A closure capture is the way a closure holds each variable it uses from around it.

In this example, the first closure only reads list. So it captures a shared borrow. list can still be printed. That print can happen before the closure runs. The second closure pushes 7 into list2. So it captures a &mut borrow. The closure variable itself must be mut. The third closure uses move. So the owned String moves into the closure. The body only prints it. The move still happens. Each closure took exactly the access its body needed, no more.

Note

A function cannot capture anything.

When a piece of code must remember values from around it, that memory is exactly what makes it a closure.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

A closure pushes items into a Vec it captured. How does it capture the Vec?