What are closures in Rust and how do Fn, FnMut, and FnOnce differ?
Understand Rust closures and how the Fn, FnMut, and FnOnce traits differ by capture mode, with clear examples for interviews and real code.
Expected Interview Answer
A closure in Rust is an anonymous function that can capture variables from the surrounding scope, and the Fn, FnMut, and FnOnce traits describe how it uses those captures: by shared reference, by mutable reference, or by taking ownership.
The compiler automatically implements the least restrictive trait a closure qualifies for based on how the body uses captured variables. FnOnce (called at most once, may consume captures) is the supertrait, FnMut adds the ability to call repeatedly while mutating, and Fn adds calling repeatedly with only immutable access. Adding the move keyword forces captures by value but does not by itself decide which Fn trait applies.
- Capture environment without manual struct plumbing
- Zero-cost: closures compile to specialized types with no heap unless boxed
- Trait bounds let APIs accept the exact call semantics they need
- Enable ergonomic iterator and callback code
- move closures make ownership transfer explicit for threads
AI Mentor Explanation
Think of a coach's instruction that references a specific player already on the field. An Fn instruction just reads the scoreboard aloud repeatedly without changing anything. An FnMut instruction updates the running total each time it is used, so it can run many times but alters state. An FnOnce instruction is like handing over the match ball to a bowler: once given it is gone and cannot be handed again.
Step-by-Step Explanation
Step 1
Define the closure
Write a |args| body block; the compiler infers the captured variables from usage.
Step 2
Determine capture mode
The compiler picks by-reference, by-mutable-reference, or by-value based on how the body uses each capture.
Step 3
Infer the trait
It implements Fn if captures are read-only, FnMut if mutated, FnOnce if consumed or moved out.
Step 4
Choose a bound
Accept the closure through an F: Fn / FnMut / FnOnce bound matching how the caller will invoke it.
Step 5
Use move when needed
Add move to force by-value capture, typically to send a closure across a thread boundary.
What Interviewer Expects
- Knowing closures capture their environment
- Explaining the three traits as a hierarchy (Fn: FnMut: FnOnce)
- Understanding that the compiler infers the least restrictive trait
- Clarifying that move affects capture-by-value, not which trait applies
- A practical example such as passing a closure to a thread or iterator
Common Mistakes
- Saying move alone makes a closure FnOnce
- Reversing the trait hierarchy relationship
- Believing every closure allocates on the heap
- Confusing capturing a reference with capturing ownership
- Thinking FnOnce closures can be called multiple times
Best Answer (HR Friendly)
“A closure in Rust is a small inline function that remembers variables from where it was written. Rust sorts closures into three kinds depending on whether they only read those variables, change them, or use them up, which lets functions ask for exactly the behaviour they need.”
Code Example
let greeting = String::from("hi");
// Fn: only reads captured data, callable many times
let say = || println!("{greeting}");
say();
say();
// FnMut: mutates a captured variable
let mut count = 0;
let mut tick = || { count += 1; println!("{count}"); };
tick();
tick();
// FnOnce: consumes the capture, callable once
let consume = move || {
let owned = greeting;
println!("consumed {owned}");
};
consume();Follow-up Questions
- How does the move keyword change what a closure captures?
- When would you box a closure as Box<dyn Fn()>?
- Why is FnOnce a supertrait of FnMut and Fn?
- How do closures differ from regular fn function pointers?
- How does the compiler represent a closure at the type level?
MCQ Practice
1. Which trait does a closure that only reads a captured variable implement?
A read-only closure satisfies Fn, and because the traits form a hierarchy it also satisfies FnMut and FnOnce.
2. What does the move keyword do to a closure?
move forces by-value capture of the environment; the resulting Fn trait still depends on how the body uses those captures.
3. A closure that moves a captured String out of its body is at most which trait?
Consuming a captured value means the closure can be called only once, so it is FnOnce.
Flash Cards
What is a Rust closure? — An anonymous function that can capture variables from its surrounding scope.
Fn trait — Closure that can be called repeatedly with only immutable access to captures.
FnMut trait — Closure that can be called repeatedly and may mutate its captured variables.
FnOnce trait — Closure callable at most once because it may consume its captures.
Effect of move — Forces captures by value; does not by itself decide the Fn/FnMut/FnOnce trait.