Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Closure capture

Closures and async blocks can refer to the places in their environment:

#![allow(unused)]
fn main() {
let mut x = 0;
let mut increment = || x += 1; // `&mut x` is captured
increment();
increment();
assert_eq!(x, 2);
}

This gets eventually compiled to a data type that stores references to, or the contents of, the captured places. The compiler determines automatically how to capture each place depending on how it is used in the closure/async block.

In this step, we use move expressions and let place to make all these captures explicit (note that this is very different from move!(..) introduced in Explicit Copies/Moves).

For every captured place, we introduce at the start of the closure a let place p = ...; that uses a move expression. The rest of the closure stays unchanged. Our initial example becomes:

#![allow(unused)]
fn main() {
let mut increment = || x = copy!(x) + 1;

// desugars to:
let mut increment = || {
    let place x = *move(&mut x);
    x = copy!(x) + 1
};
}

Another example:

#![allow(unused)]
fn main() {
let mut x = Some(42);
let mut replace = move |new: u32| Option::replace(&mut x, copy!(new));

// desugars to:
let mut replace = |new: u32| {
    let place x = move(x);
    Option::replace(&mut x, copy!(new))
};
}

This final example uses a unique immutable borrow (which we introduce in Unique-Immutable Borrow) since a &mut borrow would require let mut rx:

#![allow(unused)]
fn main() {
let mut x = 42;
let rx = &mut x;
let mut increment = || *rx += 1;

// desugars to:
let mut increment = || {
    let place rx = *move(&uniq rx);
    *rx += 1
};
}

See the Reference for details about what gets captured and how. Thanks to previous desugarings all place uses are explicit, which makes the analysis straightforward.

After this step, all closure and async block captures are explicit, using move expressions.