Fn / FnMut / FnOnce Traits
Rust closures implement one of three traits — Fn, FnMut, or FnOnce — depending on how they capture variables from the surrounding environment. When accepting a closure as a function argument, you specify its type using one of these trait bounds.
Syntax
// Fn — captures by immutable reference. Can be called any number of times.
fn call_fn<F: Fn() -> i32>(f: F) -> i32 { f() }
// FnMut — captures by mutable reference. Can be called repeatedly, but requires mut.
fn call_fnmut<F: FnMut() -> i32>(mut f: F) -> i32 { f() }
// FnOnce — takes ownership of captured values. Can only be called once.
fn call_fnonce<F: FnOnce() -> i32>(f: F) -> i32 { f() }
// Dynamic dispatch using dyn Fn()
fn call_dyn(f: &dyn Fn()) { f(); }
Trait Overview
| Trait | Description |
|---|---|
| Fn | Captures by immutable reference (&self). Can be called repeatedly. A superset of FnMut and FnOnce. |
| FnMut | Captures by mutable reference (&mut self). Can be called repeatedly. A superset of FnOnce. |
| FnOnce | Takes ownership (self) of captured values. Can only be called once. |
| move closure | Moves ownership of captured variables into the closure (commonly used when passing closures to threads). |
Sample Code
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() // Called twice.
}
fn apply_once<F: FnOnce() -> String>(f: F) -> String {
f() // Called only once.
}
fn main() {
// Fn — borrows the captured value immutably.
let base = 10;
let add_base = |x| x + base; // Borrows base as &base.
println!("Fn: {}", apply(add_base, 5)); // 15
println!("Can be called again: {}", apply(|x| x * 2, 7)); // 14
// FnMut — borrows the captured value mutably.
let mut count = 0;
let mut counter = || {
count += 1; // Borrows count as &mut count.
count
};
println!("FnMut call 1: {}", counter()); // 1
println!("FnMut call 2: {}", counter()); // 2
println!("FnMut call 3: {}", counter()); // 3
// apply_mut — calls the counter closure twice.
let mut n = 0;
let result = apply_mut(|| { n += 1; n });
println!("apply_mut result: {}", result); // 2
// FnOnce — takes ownership, so it can only be called once.
let name = String::from("Alice");
let greet = move || format!("Hello, {}!", name); // Takes ownership of name.
println!("{}", apply_once(greet)); // Hello, Alice!
// greet can no longer be called.
// move closure — passing a closure to a thread
let data = vec![1, 2, 3];
let handle = std::thread::spawn(move || {
// Moves ownership of data into the thread.
println!("Inside thread: {:?}", data);
});
handle.join().unwrap();
// Dynamic dispatch with dyn Fn()
let funcs: Vec<Box<dyn Fn(i32) -> i32>> = vec![
Box::new(|x| x + 1),
Box::new(|x| x * 2),
Box::new(|x| x * x),
];
for f in &funcs {
print!("{} ", f(3)); // 4 6 9
}
println!();
}
Notes
Rust closures capture variables from the surrounding scope in one of three ways: immutable borrow, mutable borrow, or move (transfer of ownership). Each corresponds to the Fn, FnMut, or FnOnce trait. These traits form a hierarchy: a closure that implements Fn automatically implements FnMut and FnOnce as well.
Using Box<dyn Fn()> as a parameter type allows you to store closures of different concrete types in the same collection (dynamic dispatch). When performance matters, use generic bounds like <F: Fn()> for static dispatch, which enables inlining by the compiler.
When passing a closure to a thread, you must use move to transfer ownership of captured variables. See std::thread::spawn() / join() for details.
If you find any errors or copyright issues, please contact us.