Why this matters
To accept a closure as a parameter, store one in a struct, or return one, you need to name its type. Every closure has its own unique, unnameable type, so Rust describes closures by what they can do instead, with three traits: Fn, FnMut and FnOnce. They correspond exactly to the three ways of capturing from the last lesson, and choosing the right bound makes your function accept as many closures as possible.
The three traits
FnOnce: the closure may consume captured values, so it can be called at least once. Example: amoveclosure whose body returns a capturedString.FnMut: the closure may modify captured values; it can be called many times, but calling it needs&mutaccess. Example: a closure whose body iscount += 1.Fn: the closure only reads captured values; it can be called many times, even from several places at once. Example: a closure whose body isx > limit.
They form a hierarchy: every Fn is also FnMut, and every FnMut is also FnOnce. The compiler decides which traits a closure implements from its body, not from whether you wrote move (move only decides how values get into the closure).
Taking closures as parameters
Use a generic parameter with the appropriate bound:
fn call_twice<F: Fn() -> String>(f: F) -> String {
format!("{} {}", f(), f())
}
fn call_many<F: FnMut()>(mut f: F, n: u32) {
for _ in 0..n {
f();
}
}
fn call_once<F: FnOnce() -> Vec<i32>>(f: F) -> usize {
let v = f();
v.len()
}
fn main() {
let word = String::from("echo");
println!("{}", call_twice(|| word.clone()));
let mut log = Vec::new();
call_many(|| log.push("tick"), 3);
println!("{:?}", log);
let data = vec![1, 2, 3];
println!("{}", call_once(move || data));
}
echo echo
["tick", "tick", "tick"]
3
How to choose the bound for your parameter: ask for as little as you need.
- Call it once? Accept
FnOnce: then every closure qualifies. - Call it repeatedly?
FnMut: accepts everything except consuming closures. - Call it repeatedly through a shared reference, or from several threads?
Fn.
The standard library follows this: Option::unwrap_or_else takes FnOnce (called at most once), Vec::retain takes FnMut, and thread::spawn takes FnOnce plus Send + 'static.
Passing a closure that does not satisfy the bound is a clear error:
fn call_twice<F: Fn()>(f: F) {
f();
f();
}
fn main() {
let mut count = 0;
call_twice(|| count += 1);
println!("{count}");
}
error[E0594]: cannot assign to `count`, as it is a captured variable in a `Fn` closure
--> main.rs:8:19
|
1 | fn call_twice<F: Fn()>(f: F) {
| - change this to accept `FnMut` instead of `Fn`
...
8 | call_twice(|| count += 1);
| ---------- -- ^^^^^^^^^^ cannot assign
| | |
| | in this closure
| expects `Fn` instead of `FnMut`
Ordinary functions work too
A named function can be passed wherever a closure is expected, since functions implement all three traits:
fn double(x: i32) -> i32 {
x * 2
}
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(x)
}
fn main() {
println!("{}", apply(double, 21));
let words = ["a", "bb", "ccc"];
let lens: Vec<usize> = words.iter().map(|w| w.len()).collect();
let upper: Vec<String> = words.iter().map(|w| w.to_uppercase()).collect();
let strs: Vec<String> = [1, 2].iter().map(ToString::to_string).collect();
println!("{:?} {:?} {:?}", lens, upper, strs);
}
42
[1, 2, 3] ["A", "BB", "CCC"] ["1", "2"]
There is also the plain function pointer type fn(i32) -> i32 (lowercase fn), which can hold named functions and closures that capture nothing. It is a single concrete type, handy in tables, but cannot hold capturing closures.
Storing closures: generics or Box<dyn Fn>
A struct can hold a closure either through a type parameter (fast, one type per struct instance) or a trait object (any closure, chosen at run time):
struct Button<F: Fn() -> String> {
label: String,
on_click: F,
}
struct Menu {
items: Vec<(String, Box<dyn Fn() -> String>)>,
}
fn main() {
let b = Button { label: String::from("OK"), on_click: || String::from("confirmed") };
println!("{}: {}", b.label, (b.on_click)());
let user = String::from("ada");
let menu = Menu {
items: vec![
(String::from("greet"), Box::new(move || format!("hello {user}"))),
(String::from("time"), Box::new(|| String::from("noon"))),
],
};
for (name, action) in &menu.items {
println!("{name} -> {}", action());
}
}
OK: confirmed
greet -> hello ada
time -> noon
Note (b.on_click)(): the parentheses tell Rust to call the field, not a method named on_click. The Menu needs Box<dyn Fn> because its closures have different types. Same trade-off as module 10: generics for speed and single types, dyn for mixing.
Returning closures
Return impl Fn... for a single closure type, Box<dyn Fn...> when different branches return different closures:
fn adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n
}
fn op(name: &str) -> Box<dyn Fn(i32, i32) -> i32> {
match name {
"add" => Box::new(|a, b| a + b),
"mul" => Box::new(|a, b| a * b),
_ => Box::new(|_, _| 0),
}
}
fn main() {
let add5 = adder(5);
println!("{} {}", add5(1), op("mul")(6, 7));
}
6 42
Common mistakes
- Bound too strict: requiring
Fnwhen you call once rejects perfectly good closures. UseFnOnce. - Forgetting
mut ffor anFnMutparameter: E0596 "cannot borrowfas mutable". - Calling an
FnOncetwice: E0382 "use of moved value:f", because calling consumes it. - Different closures from
-> impl Fn: E0308 "no two closures, even if identical, have the same type", with a hint to box them.
Recap
Fnreads captures,FnMutmodifies them,FnOncemay consume them;FnimpliesFnMutimpliesFnOnce.- The body decides which traits a closure implements;
moveonly decides how captures are taken. - Accept closures with generic bounds, asking for the weakest trait you need.
- Named functions and
fnpointers also work where closures are expected. - Store or return closures with generics/
impl Fn, orBox<dyn Fn>for mixed types.
Going further: read the docs for std::ops::FnOnce, FnMut and Fn, and look at the signatures of Option::map, Vec::sort_by and std::thread::spawn to see which trait each requires.