Skip to content

Closures

A closure is an anonymous function defined inline with the |params| body syntax. Unlike a regular fn, a closure can capture variables from its surrounding scope.

let multiplier = 3;
let triple = |x| x * multiplier; // captures multiplier from the environment
println!("{}", triple(7)); // 21

Rust has three closure traits that describe how a closure uses its captured values:

TraitHow it capturesCan be called
FnBorrows immutably (&T)Any number of times
FnMutBorrows mutably (&mut T)Any number of times
FnOnceTakes ownership (T)Exactly once

Every closure implements at least FnOnce. If it only borrows, it also implements Fn.

fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }
let base = 10;
let add_base = |x| x + base; // borrows base immutably
println!("{}", apply(add_base, 5)); // 15
let mut count = 0;
let mut increment = || { count += 1; count };
println!("{}", increment()); // 1
println!("{}", increment()); // 2
drop(increment); // release the mutable borrow
println!("count is {}", count); // 2

drop(increment) must be called before reading count directly, because increment holds a &mut count that would conflict with the immutable borrow in println!.

let name = String::from("Rust");
let greet = || println!("Hello, {}!", name); // moves name into closure
greet(); // name is consumed here
// greet(); // would compile-error — FnOnce can only be called once

move forces the closure to take ownership of all captured variables, regardless of whether it would otherwise only borrow:

let message = String::from("hi");
let say = move || println!("{}", message); // message moved into say
// println!("{}", message); // error — message was moved
say();

move is essential when the closure outlives its defining scope (e.g., passing it to a thread).

Iterator adaptors like map and filter accept closures directly:

let threshold = 3;
let big: Vec<i32> = vec![1, 2, 3, 4, 5]
.iter()
.filter(|&&x| x > threshold) // closure captures threshold by &
.map(|&x| x * 10)
.collect();
// [40, 50]
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(x)
}
fn apply_mut<F: FnMut() -> i32>(mut f: F) -> i32 {
f() + f()
}
fn apply_once<F: FnOnce() -> String>(f: F) -> String {
f()
}
fn main() {
// Fn: borrows environment immutably
let multiplier = 3;
let triple = |x| x * multiplier;
println!("apply triple to 5: {}", apply(triple, 5));
// FnMut: captures and mutates
let mut count = 0;
let mut increment = || { count += 1; count };
let result = apply_mut(&mut increment);
drop(increment); // release mutable borrow before reading count
println!("FnMut result: {}, count is now: {}", result, count);
// FnOnce + move: takes ownership
let name = String::from("Rust");
let greeting = move || format!("Hello, {}!", name);
println!("{}", apply_once(greeting));
// closures as iterator adaptors
let nums = vec![1, 2, 3, 4, 5];
let threshold = 3;
let big: Vec<i32> = nums
.iter()
.filter(|&&x| x > threshold)
.map(|&x| x * 10)
.collect();
println!("big: {:?}", big);
}
What closure trait describes a closure that mutably borrows a captured variable?
Why must drop(increment) be called before println!("{}", count) in an FnMut example?
What does the move keyword do to a closure?
A closure that only reads a captured variable without modifying it implements which trait(s)?