Closures
What Is a Closure?
Section titled “What Is a Closure?”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 environmentprintln!("{}", triple(7)); // 21Capturing the Environment
Section titled “Capturing the Environment”Rust has three closure traits that describe how a closure uses its captured values:
| Trait | How it captures | Can be called |
|---|---|---|
Fn | Borrows immutably (&T) | Any number of times |
FnMut | Borrows mutably (&mut T) | Any number of times |
FnOnce | Takes ownership (T) | Exactly once |
Every closure implements at least FnOnce. If it only borrows, it also implements Fn.
Fn — Immutable Borrow
Section titled “Fn — Immutable Borrow”fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }
let base = 10;let add_base = |x| x + base; // borrows base immutablyprintln!("{}", apply(add_base, 5)); // 15FnMut — Mutable Borrow
Section titled “FnMut — Mutable Borrow”let mut count = 0;let mut increment = || { count += 1; count };println!("{}", increment()); // 1println!("{}", increment()); // 2drop(increment); // release the mutable borrowprintln!("count is {}", count); // 2drop(increment) must be called before reading count directly, because increment holds a &mut count that would conflict with the immutable borrow in println!.
FnOnce — Takes Ownership
Section titled “FnOnce — Takes Ownership”let name = String::from("Rust");let greet = || println!("Hello, {}!", name); // moves name into closuregreet(); // name is consumed here// greet(); // would compile-error — FnOnce can only be called onceThe move Keyword
Section titled “The move Keyword”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 movedsay();move is essential when the closure outlives its defining scope (e.g., passing it to a thread).
Closures as Iterator Adaptors
Section titled “Closures as Iterator Adaptors”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);}Compiling…