Closures
Prerequisites: Lifetimes
A closure is an anonymous function you can store in a variable or pass to
another function. Unlike a normal fn, a closure can capture variables
from the scope where it is defined.
Syntax
fn main() {
let add_one = |x: i32| -> i32 { x + 1 }; // fully annotated
let add_two = |x| x + 2; // types inferred, braces optional for one expression
let say_hi = || println!("hi"); // no parameters
println!("{} {}", add_one(1), add_two(1));
say_hi();
}
Parameter and return types are usually inferred from how the closure is used. Once inferred, they are fixed: a closure has exactly one set of types.
Capturing
A closure captures each variable in the least demanding way that works:
- By shared reference (
&T) if it only reads the variable. - By mutable reference (
&mut T) if it modifies it. - By value (moving it in) if it needs ownership, for example to drop it or return it.
Writing move before the closure forces capture by value for everything it
uses. That is required when the closure must outlive the current scope, such
as when it is returned from a function or sent to another thread.
Fn, FnMut and FnOnce
How a closure uses its captures decides which traits it implements, and those traits are how functions accept closures:
| Trait | Can be called | Closure body... |
|---|---|---|
FnOnce |
at least once | may move captured values out (every closure is FnOnce) |
FnMut |
many times | may modify captured values |
Fn |
many times, even concurrently | only reads captured values |
When writing a function that takes a closure, ask for the least
restrictive trait you need: FnOnce if you call it once, FnMut if you call
it repeatedly, and Fn only if you need to call it through a shared reference.
Example
fn apply_twice(f: impl Fn(i32) -> i32, x: i32) -> i32 {
f(f(x))
}
fn repeat(times: usize, mut action: impl FnMut()) {
for _ in 0..times {
action();
}
}
fn consume(f: impl FnOnce() -> String) -> String {
f()
}
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor // `move`: the closure keeps its own copy of `factor`
}
fn main() {
// Fn: reads `offset` by shared reference.
let offset = 10;
let add_offset = |x| x + offset;
println!("{}", apply_twice(add_offset, 1));
// FnMut: modifies `count` through a mutable reference.
let mut count = 0;
repeat(3, || count += 1);
println!("count = {count}");
// FnOnce: moves `name` out of the closure, so it can only run once.
let name = String::from("Ferris");
let greeting = consume(move || name + " says hi");
println!("{greeting}");
// Returning a closure.
let triple = make_multiplier(3);
println!("{}", triple(7));
// Closures stored in a Vec need a common type: Box<dyn Fn>.
let ops: Vec<(&str, Box<dyn Fn(i32) -> i32>)> = vec![
("double", Box::new(|x| x * 2)),
("square", Box::new(|x| x * x)),
("negate", Box::new(|x| -x)),
];
for (label, op) in &ops {
println!("{label}(5) = {}", op(5));
}
// Closures are what most iterator and Option methods take.
let mut words = vec!["pear", "fig", "banana"];
words.sort_by_key(|w| w.len());
println!("{words:?}");
}
Expected output
21
count = 3
Ferris says hi
21
double(5) = 10
square(5) = 25
negate(5) = -5
["fig", "pear", "banana"]
apply_twice(add_offset, 1) computes (1 + 10) + 10 = 21.
Common mistakes
-
Using one closure with two different types:
let id = |x| x; id(5); id("five");fails witherror[E0308]: mismatched types, because the first call fixedxas an integer. -
Calling a closure that mutates a capture without declaring the closure variable
mut:fn main() { let mut total = 0; let add = |n: i32| total += n; add(5); println!("{total}"); }This fails with
error[E0596]: cannot borrow `add` as mutable, as it is not declared as mutable. Writelet mut add = .... -
Using a captured variable while a closure still holds a mutable borrow of it. The borrow lasts until the closure's last use, so finish with the closure first.
-
Forgetting
movewhen returning a closure that captures a local:error[E0373]: closure may outlive the current function, but it borrows `factor`, which is owned by the current function.
Practice
Write fn make_counter() -> impl FnMut() -> u32 that returns a closure which
returns 1, 2, 3, … on successive calls. Call it three times and print the
results on one line.
Show solution
fn make_counter() -> impl FnMut() -> u32 {
let mut count = 0;
move || {
count += 1;
count
}
}
fn main() {
let mut next = make_counter();
let a = next();
let b = next();
let c = next();
println!("{a} {b} {c}");
}
1 2 3