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));  // 6

With 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));  // true

A 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 usable

move 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:

TraitWhat it means
Fnborrows captured variables immutably — can be called multiple times
FnMutborrows captured variables mutably — can be called multiple times
FnOncetakes 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));  // 10

In 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.