FnOnce / FnMut / Fn
Goal of This Episode
Understand that FnOnce, FnMut, and Fn are traits rather than types, grasp their inheritance relationships, and learn to choose the right closure trait.
Concept
They’re traits, Not Types
We’ve been saying FnOnce, FnMut, and Fn for several episodes without formally explaining — they are in fact traits. Like the Clone and Display you’ve already met, FnOnce / FnMut / Fn are traits defined in the standard library. Each closure’s anonymous struct automatically impls the corresponding traits (last episode’s inference rules decide which).
So what do these traits look like?
FnOnce(Args) -> Ret: callable once (the call may consume the closure, so further calls are not guaranteed).FnMut(Args) -> Ret: callable repeatedly, with mutable access to captured state.Fn(Args) -> Ret: callable repeatedly, without mutable access to captured state.
Watch out! fn(i32) -> i32 (lowercase) is the function pointer type, while Fn(i32) -> i32 (capitalized) is a trait. Two entirely different things.
The Inheritance Relationships
The three traits form supertrait relationships:
Fn : FnMut : FnOnce
Meaning:
- Everything implementing
Fnautomatically implementsFnMutandFnOnce. - Everything implementing
FnMutautomatically implementsFnOnce. - But
FnOncedoesn’t implyFnMut, norFnMutimplyFn.
Why this direction?
Fn→FnMut: if a closure runs with just&self, using&mut selfcertainly works too (it simply uses a mutable reference where a shared one would have sufficed).FnMut→FnOnce: if a closure runs with&mut self, handing itself(full ownership) certainly works — owning a thing includes being able to modify it. It’s just that after the call thestructis consumed, so no second call.
The reverse doesn’t hold — a closure that must consume itself (FnOnce) can’t promise repeated calls (FnMut).
Accepting Closures with impl Trait
Remember Chapter 5’s impl Trait? Use it to accept closure parameters:
fn call_once(f: impl FnOnce() -> String) -> String {
f()
}
fn call_many_times(mut f: impl FnMut()) {
f();
f();
f();
}
fn call_twice(f: impl Fn() -> i32) -> i32 {
f() + f()
}
fn main() {}
Note the mut on the FnMut parameter — calling an FnMut closure needs &mut self, so f itself must be mut.
Design Principle: Pick the Bound Accepting the Most Closures
When designing a function that takes a closure, choose the trait bound accepting the widest range of closures:
- Try
FnOncefirst — if you only call it once. - Move to
FnMut— if you need repeated calls. - Only then
Fn— if you need repeated calls without a mutable reference to the closure value.
Why? Because FnOnce accepts every closure (every closure implements FnOnce), while Fn accepts only closures callable through a shared reference. The widest bound gives callers maximum freedom.
In practice Fn is rarely needed — most functions calling a closure repeatedly do fine with FnMut (which also accepts Fn closures). Use Fn when the function needs to call the closure without a mutable reference to the closure value.
Function Pointers Implement All Three traits Too
Ordinary functions (and function pointers fn) automatically implement Fn, FnMut, and FnOnce. So a function name can be passed anywhere these three traits are accepted.
Example Code
// Only one call needed → FnOnce (accepts the most closures)
fn consume_and_print(f: impl FnOnce() -> String) {
let result = f();
println!("Result: {}", result);
}
// Repeated calls needed → FnMut
fn repeat_three_times(mut f: impl FnMut()) {
f();
f();
f();
}
// Repeated calls without a mutable reference to the closure value → Fn
fn sum_two_calls(f: impl Fn(i32) -> i32, x: i32) -> i32 {
f(x) + f(x)
}
fn main() {
// FnOnce: the closure consumes a captured value
let name = String::from("Rust");
consume_and_print(|| {
let s = name; // Moves name
format!("Hello, {}!", s)
});
// FnMut: the closure modifies a captured variable
let mut count = 0;
repeat_three_times(|| {
count += 1;
println!("Call number {}", count);
});
println!("Called {} times in total", count);
// Fn: the closure only reads
let multiplier = 3;
let result = sum_two_calls(|x| x * multiplier, 5);
println!("sum_two_calls result: {}", result);
// Ordinary functions can be passed in too
fn double(x: i32) -> i32 {
x * 2
}
let result2 = sum_two_calls(double, 10);
println!("With an ordinary function: {}", result2);
// An Fn closure also fits an FnOnce parameter (every Fn closure implements FnOnce)
let greeting = String::from("Hi");
consume_and_print(|| {
format!("{}, world!", greeting) // Only reads greeting — it's Fn
});
// greeting survives, since the closure merely borrowed it
println!("greeting is still here: {}", greeting);
}
Recap
FnOnce,FnMut,Fnaretraits, not types;fnis the function pointer type.- The inheritance:
Fn⊂FnMut⊂FnOnce(FnOnceaccepts every closure). - Accept closure parameters with
impl FnOnce()/impl FnMut()/impl Fn(). FnMutparameters needmut.- Design principle for closure-taking functions: start with
FnOnce, switch toFnMutfor repeated calls, and useFnwhen calls must not require a mutable reference to the closure value. - Function pointers automatically implement
Fn+FnMut+FnOnce.