🎓 A1Learner
🔍

Rust Closures

A closure is a small anonymous function. A closure can capture variables from the code around it.

Write a closure with bars

The inputs go between the bars, and the body follows. Types are usually guessed from how the closure is used, so you rarely write them:

let double = |x| x * 2;
println!("{}", double(21));

A closure captures what it uses

A closure captures the variables it uses from around it. times only reads factor, so it captures by shared borrow. It changes nothing it captured, so it is an Fn closure. In short, a closure is an unnamed function that can use the variables around it:

let factor = 4;
let times = |n: i32| n * factor;
println!("{}", times(5));

Capture mutably to change a value

To change a captured variable, the closure captures it by &mut borrow. A closure that changes its captured variables is FnMut, and it must itself be mut:

let mut counter = 10;
let mut bump = || { counter += 1; counter };
println!("{} {}", bump(), bump());

move takes ownership

Write move to make the closure take ownership of what it captures, instead of borrowing:

let name = String::from("Ada");
let greet = move || println!("hello {}", name);
greet();

FnOnce: usable only once

An FnOnce closure uses up what it captured, so it can run only once. This one hands back the captured String itself — a second take() call would not compile:

let name = String::from("Ada");
let take = move || name;
println!("{}", take());

Example

fn main() {
    let factor = 3;
    let times = |n: i32| n * factor;
    println!("{}", times(7));
    let mut counter = 0;
    let mut bump = || { counter += 1; counter };
    println!("{} {}", bump(), bump());
}

Key points

  • Capture by shared borrow to only read, by &mut borrow to change, or by move to take ownership
  • A closure is written where it is used.
  • Capturing is the talent a plain function does not have.

In this example, factor is 3. The closure times takes one input n and returns n * factor. It uses factor from the code around it. That factor is captured by shared borrow. times(7) prints 21. Below, counter starts at 0. The closure bump adds 1 to counter and returns the new value. Because bump changes a captured variable, it is an FnMut closure, and bump itself must be declared mut. Calling it twice prints 1, then 2. The Threads lesson relies on move closures owning everything they need.

Note

The move kind matters for threads.

A move closure takes its data with it.

So the thread owns everything it needs.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What does the move keyword do on a closure?