title: Closures
A closure is an anonymous function that can capture variables from its surrounding scope. You’ve already seen them used with iterators — the |n| n * 2 passed to .map() is a closure.
Basic syntax
let add_one = |x| x + 1;
println!("{}", add_one(5)); // 6With explicit types (usually not needed — Rust infers them):
let add_one = |x: i32| -> i32 { x + 1 };Multi-line closures use a block:
let describe = |n: i32| {
if n > 0 { "positive" } else { "negative" }
};Capturing from the environment
Unlike regular functions, closures can use variables from the scope they’re defined in:
let threshold = 10;
let is_big = |n| n > threshold; // captures threshold
println!("{}", is_big(5)); // false
println!("{}", is_big(15)); // trueA regular function can’t do this — it can only use its own parameters and globals.
How closures capture
Rust captures variables in the least restrictive way possible:
let name = String::from("Alice");
let greet = || println!("Hello, {name}!"); // borrows name
greet();
println!("{name}"); // name is still usablemove closures
Use move to force the closure to take ownership of captured variables. Necessary when the closure needs to outlive the scope it was created in (e.g. when passing to a thread):
let name = String::from("Alice");
let greet = move || println!("Hello, {name}!");
// name has been moved into the closure
// println!("{name}"); // ERROR — name is gone
greet();Closures with iterators
This is the most common use of closures in Rust:
let numbers = vec![1, 2, 3, 4, 5];
let result: Vec<i32> = numbers
.iter()
.filter(|&&n| n % 2 == 0)
.map(|&n| n * 10)
.collect();
// [20, 40]Closures as function parameters
Functions can accept closures using trait bounds. There are three closure traits:
| Trait | What it means |
|---|---|
Fn | borrows captured variables immutably — can be called multiple times |
FnMut | borrows captured variables mutably — can be called multiple times |
FnOnce | takes ownership of captured variables — can only be called once |
fn apply<F: Fn(i32) -> i32>(f: F, value: i32) -> i32 {
f(value)
}
let double = |x| x * 2;
println!("{}", apply(double, 5)); // 10In practice, use Fn unless you have a reason to be more specific — it accepts the widest range of closures.
Closures vs functions
Regular functions and closures are interchangeable when no capturing is needed:
fn double(x: i32) -> i32 { x * 2 }
let v: Vec<i32> = vec![1, 2, 3].iter().map(double).collect();
// same as .map(|x| x * 2)Use a named function when the logic is complex or reused. Use a closure when it’s short and local.