gotcharustModerate
Closures and Fn traits: Fn, FnMut, FnOnce differences
Viewed 0 times
closureFnFnMutFnOncecapturemovehigher-order functions
Error Messages
Problem
Functions that accept closures fail to compile because the wrong Fn trait bound is used, or captured variables cause unexpected moves.
Solution
Choose the right Fn trait based on how the closure uses captured variables:
// Fn — borrows captured variables immutably, can be called repeatedly
fn call_twice<F: Fn()>(f: F) { f(); f(); }
let msg = String::from("hello");
call_twice(|| println!("{}", msg)); // borrows msg
// FnMut — borrows captured variables mutably, can be called repeatedly
fn call_and_count<F: FnMut()>(mut f: F) { f(); f(); f(); }
let mut count = 0;
call_and_count(|| count += 1);
println!("Called {} times", count);
// FnOnce — consumes captured variables, can only be called once
fn consume<F: FnOnce()>(f: F) { f(); }
let data = String::from("important");
consume(move || drop(data)); // data is moved and dropped
// move keyword forces capture by value
let name = String::from("Alice");
std::thread::spawn(move || println!("Hello {}", name)); // name moved into threadWhy
Closures automatically infer the most permissive trait. FnOnce is the most general (all closures implement it), Fn is the most restrictive (closures that only borrow). Rust picks the tightest fit based on the closure body.
Gotchas
- A closure that moves a captured variable implements FnOnce but not Fn or FnMut
- The move keyword forces all captures to be by value — needed for closures sent to threads
- impl Fn() in return position requires boxing: Box<dyn Fn()> or use a named generic
Revisions (0)
No revisions yet.