Closures
The Fn Trait
The Fn trait in std::ops is the most capability-rich of the three closure traits, representing closures and functions that can be called repeatedly through a shared immutable reference (&self) without mutating or consuming their captured environment.
Conceptual Definition
pub trait Fn<Args>: FnMut<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
Because Fn takes &self:
- An
Fnclosure can be called any number of times, including concurrently across multiple threads (whenSync) or recursively via shared references. Fnis a subtrait ofFnMut(which in turn is a subtrait ofFnOnce), meaning anyFnclosure can also be passed whereverFnMutorFnOnceis required.
Which Callables Implement Fn
| Callable Kind | Implements Fn? |
Reason |
|---|---|---|
Plain functions (fn) and non-capturing closures |
Yes | No captured state to mutate or consume |
Closure capturing variables only by shared reference (&T) |
Yes | Reading &T through &self is always valid |
move closure that owns T and only reads T in its body |
Yes | Owning T inside the closure struct still allows reading &self.t via &self |
| Closure that mutates or moves out of a captured variable | No | Requires &mut self (FnMut) or self (FnOnce) |
Calling an Fn Closure Repeatedly and Through Shared References
fn run_twice<F>(f: &F, input: i32) -> (i32, i32)
where
F: Fn(i32) -> i32,
{
(f(input), f(input + 1))
}
fn main() {
let offset = 100;
// Captures `offset` by shared reference -> implements `Fn(i32) -> i32`
let add_offset = |x: i32| x + offset;
let (first, second) = run_twice(&add_offset, 5);
println!("first = {first}, second = {second}");
// `move` closure that owns a `String` but only reads it ALSO implements `Fn`:
let prefix = String::from("ID");
let format_id = move |id: u32| format!("{prefix}-{id:04}");
println!("{}, {}", format_id(1), format_id(2));
}
first = 105, second = 106
ID-0001, ID-0002
Notice the distinction between how a closure captures (move vs. borrow) and which Fn* traits it implements: format_id above is a move closure that owns prefix, yet it still implements Fn because its body only reads prefix (&self.prefix).