Question
In Rust, what specific conditions determine whether a closure implements the Fn, FnMut, and FnOnce traits?
More specifically:
- When does a closure fail to implement
FnOnce? - When does a closure fail to implement
FnMut? - When does a closure fail to implement
Fn?
For example, if a closure mutates captured state inside its body, the compiler will not allow it to implement Fn. I want to understand the exact rules the compiler uses for all three closure traits.
Short Answer
By the end of this page, you will understand how Rust decides whether a closure implements Fn, FnMut, and FnOnce. You will learn that every closure implements FnOnce, but only some also implement FnMut, and only a smaller set implement Fn. The key idea is how the closure uses its captured variables: moving values out limits it to FnOnce, mutating captured values allows FnMut but not Fn, and only read-only access allows Fn.
Concept
Rust closures automatically implement one, two, or all three of the closure traits based on how they use captured values.
The three traits form a ladder:
Fn= can be called many times without mutating or consuming captured valuesFnMut= can be called many times, but may mutate captured valuesFnOnce= can be called at least once, and may consume captured values
A very important rule is:
- Every closure implements
FnOnce
That means the answer to "When does a closure not implement FnOnce?" is:
- Never for normal closures
Why? Because any closure can always be called once by consuming the closure itself.
The core rule
Rust looks at what the closure body does with the variables it captures from the surrounding scope.
A closure implements Fn if
- it does not move captured values out of itself
- it does not mutably borrow or mutate captured values
- it only reads captured values, or captures nothing
A closure implements FnMut if
- it does not move captured values out of itself
- but it may mutate captured values or take mutable borrows of them
A closure implements FnOnce if
- it may move captured values out of itself
- or otherwise consume captured state during the call
Why this matters
These traits are used everywhere in Rust APIs:
- iterator adapters like
.map()and.filter() - callbacks
- event handlers
- retry logic
- thread spawning
- one-time initialization
If a function expects an Fn, you can pass only closures that are safe to call repeatedly without changing or consuming their environment. If it expects FnMut, mutation is allowed. If it expects FnOnce, the closure may consume what it captured.
Trait relationship
The traits are related like this:
Fnis the most restrictiveFnMutis less restrictiveFnOnceis the least restrictive
In practice:
- if a closure implements
Fn, it also implementsFnMutandFnOnce - if a closure implements
FnMut, it also implementsFnOnce - if a closure implements only
FnOnce, it cannot be reused safely
So the main question is usually not whether a closure implements FnOnce, but whether it is also reusable as FnMut or Fn.
Mental Model
Think of a closure as a small backpack that carries values from the surrounding scope.
When you call the closure, Rust asks: what does this closure do with the items in its backpack?
Fn: it only looks at the itemsFnMut: it changes the items in the backpackFnOnce: it takes an item out permanently and uses it up
Analogy
Imagine a lunchbox:
Fnopens the lunchbox, looks inside, maybe reads the label, then closes it unchangedFnMutopens the lunchbox and rearranges or modifies what is insideFnOnceopens the lunchbox and eats the sandwich, so the same lunchbox content cannot be used the same way again
That is why FnOnce may be callable only once: the closure may consume part of its captured state.
Syntax and Examples
Basic syntax
The traits are usually seen in generic bounds:
fn call_fn<F: Fn()>(f: F) {
f();
}
fn call_fnmut<F: FnMut()>(mut f: F) {
f();
}
fn call_fnonce<F: FnOnce()>(f: F) {
f();
}
Example 1: Closure that implements Fn
fn main() {
let name = String::from("Rust");
let print_name = || println!("Hello, {name}!");
print_name();
print_name();
}
This closure only reads name.
- It does not mutate
name - It does not move
nameout of the closure
So it implements:
FnFnMutFnOnce
Example 2: Closure that implements FnMut but not Fn
fn main() {
let mut count = 0;
let mut increment = || {
count += 1;
println!("count = {count}");
};
increment();
increment();
}
This closure mutates captured state.
- It can still be called multiple times
- But because it mutates its environment, it does not implement
Fn
So it implements:
FnMutFnOnce
But not:
Fn
Example 3: Closure that implements only FnOnce
fn main() {
let text = String::from("hello");
let consume = || {
let moved = text;
println!("{moved}");
};
consume();
}
This closure moves text out of its captured environment when called.
After that, the captured value is gone.
So it implements:
FnOnce
But not:
FnMutFn
Example 4: A move closure does not automatically mean FnOnce
fn main() {
let x = 10;
let print_x = move || println!("x = {x}");
print_x();
print_x();
}
Even though this is a move closure, it only reads x.
move affects how values are captured, not automatically how they are used later.
This closure can still implement Fn.
That is an important beginner point:
movedoes not always meanFnOnce- moving a captured value out inside the body is what forces
FnOnce
Step by Step Execution
Consider this closure:
fn main() {
let mut total = 0;
let mut add_one = || {
total += 1;
println!("total = {total}");
};
add_one();
add_one();
}
Step-by-step
1. total is created
let mut total = 0;
A mutable integer exists in the surrounding scope.
2. The closure is created
let mut add_one = || {
total += 1;
println!("total = {total}");
};
Rust sees that the closure uses total and captures it.
Because the closure does this:
total += 1;
it must capture total in a way that allows mutation.
3. Rust determines the trait
The closure:
- does not move
totalout - does mutate captured state
So:
Fnis not allowedFnMutis allowedFnOnceis also allowed
4. First call
add_one();
The closure runs:
totalchanges from0to1- prints
total = 1
5. Second call
add_one();
The closure still has access to the same captured total:
totalchanges from1to2- prints
total = 2
Why not Fn?
Because Fn requires calling through an immutable reference to the closure. If the closure needs to change its captured state, immutable access is not enough.
Why still FnOnce?
Because anything callable as FnMut is also callable once by value.
Real World Use Cases
1. Read-only callbacks with Fn
Use Fn when a callback should be safe to call many times without changing its environment.
Examples:
- formatting log messages
- checking whether a feature flag is enabled
- filtering items using shared read-only configuration
fn run_twice<F: Fn()>(f: F) {
f();
f();
}
2. Stateful processing with FnMut
Use FnMut when the closure needs to keep or update state between calls.
Examples:
- counting how many items were processed
- building up metrics
- assigning sequence numbers
- caching simple intermediate state
let mut count = 0;
let mut process = |_| {
count += 1;
};
This is common in iterator pipelines.
3. One-time ownership transfer with FnOnce
Use FnOnce when the closure needs to consume something it captured.
Examples:
- moving a
Stringinto a worker task - transferring ownership into a thread
- one-time cleanup logic
- deferred execution that consumes resources
let data = String::from("job data");
let job = move || {
println!("processing {data}");
};
If the closure consumes data, it may only be callable once.
Real Codebase Usage
In real Rust projects, developers choose closure trait bounds based on how often the callback should be reusable and whether it needs state.
Common patterns
Guard-like read-only checks use Fn
fn validate_with<F: Fn(&str) -> bool>(input: &str, check: F) -> bool {
check(input)
}
This is useful for:
- validation rules
- predicates
- configuration checks
- access-control checks
Iteration counters and accumulators use FnMut
Many iterator methods accept FnMut because the closure is called multiple times and often updates state.
fn main() {
let items = vec![1, 2, 3];
let mut sum = 0;
items.iter().for_each(|x| {
sum += x;
});
println!("sum = {sum}");
}
The closure mutates sum, so FnMut is the right fit.
Deferred ownership transfer uses FnOnce
APIs that run a task one time often accept FnOnce.
fn run_job<F: FnOnce()>(job: F) {
job();
}
This is common when:
- starting a thread
- executing a one-time task
- moving owned data into a callback
- finalizing resources
Practical API design tip
When writing your own functions, prefer the least restrictive bound that still works:
- use
FnOnceif the function calls the closure once - use
FnMutif the function calls it multiple times and mutation may be useful - use
Fnif the function calls it multiple times and wants to allow only read-only closures
This makes your API more flexible for callers.
Common Mistakes
Mistake 1: Thinking some closures do not implement FnOnce
This is the biggest misconception.
let x = 5;
let c = || println!("{x}");
This closure implements FnOnce too.
Rule
- All closures implement
FnOnce - Some also implement
FnMut - Some also implement
Fn
Mistake 2: Assuming move means only FnOnce
Broken assumption:
let x = String::from("hello");
let c = move || println!("{x}");
Beginners often think this must be FnOnce only.
But this closure only reads x, so it can still be called multiple times.
Fix
Focus on what happens inside the body, not only how capture happens.
Mistake 3: Mutating captured state and expecting Fn
let mut count = 0;
let add = || {
count += 1;
};
This closure cannot implement Fn because it mutates captured state.
Fix
Use an FnMut bound when your closure needs to change its environment.
Mistake 4: Moving out of a captured value accidentally
let text = String::from("hello");
let consume = || {
let taken = text;
println!("{taken}");
};
This consumes text when called, so the closure becomes FnOnce only.
Fix
Borrow instead of moving if you want repeated calls:
let text = String::from("hello");
let show = || {
println!("{text}");
};
Mistake 5: Forgetting that calling an FnMut closure requires a mutable binding
Broken code:
let mut count = 0;
let increment = || {
count += 1;
};
increment();
This fails because calling an FnMut closure requires mutable access to the closure value.
Fix
let mut count = 0;
let mut increment = || {
count += 1;
};
increment();
The closure variable itself must be mut.
Comparisons
Closure traits compared
| Trait | Can be called more than once? | Can mutate captured values? | Can move captured values out? |
|---|---|---|---|
Fn | Yes | No | No |
FnMut | Yes | Yes | No |
FnOnce | Not necessarily | Yes, if owned appropriately | Yes |
Relationship between the traits
| If a closure implements... | Then it also implements... |
|---|---|
Fn | FnMut, FnOnce |
FnMut | FnOnce |
FnOnce | nothing more is guaranteed |
move vs closure traits
| Idea | Meaning |
|---|---|
move closure | Captures values by ownership when possible |
FnOnce closure | May consume captured values when called |
These are related but not identical.
Choosing a function bound
| If your function... | Preferred bound |
|---|---|
| calls the closure once | FnOnce |
| calls it multiple times and allows mutation | FnMut |
| calls it multiple times and needs read-only behavior | Fn |
Cheat Sheet
Quick rules
- Every closure implements
FnOnce - A closure implements
FnMutif it does not move captured values out of itself - A closure implements
Fnif it does not mutate captured values and does not move them out
Easy way to decide
Ask these questions in order:
- Does the closure move a captured value out when called?
- Yes -> only
FnOnce - No -> continue
- Yes -> only
- Does the closure mutate captured state?
- Yes ->
FnMutandFnOnce - No ->
Fn,FnMut, andFnOnce
- Yes ->
Trait ladder
Fn -> FnMut -> FnOnce
Meaning:
- anything that is
Fncan be used whereFnMutis expected - anything that is
FnMutcan be used whereFnOnceis expected
Common examples
Read only
let x = 1;
let c = || println!("{x}");
Implements: Fn, FnMut, FnOnce
Mutates capture
let mut x = 1;
let mut c = || x += 1;
Implements: FnMut, FnOnce
Consumes capture
let s = String::from("hi");
let c = || drop(s);
Implements: FnOnce
Important note about move
let s = String::from("hi");
let c = move || println!("{s}");
This may still implement Fn.
move changes capture mode, not necessarily call trait behavior.
FAQ
Does every Rust closure implement FnOnce?
Yes. Every closure can be called at least once by consuming the closure value itself.
When does a closure implement only FnOnce?
When calling it moves a captured value out of the closure or otherwise consumes captured state.
Why does mutating captured state prevent Fn?
Because Fn is called through an immutable reference. If the closure needs to change its captured environment, immutable access is not enough.
Does move always make a closure FnOnce?
No. A move closure can still implement Fn or FnMut if it does not consume captured values during the call.
Why do many iterator methods use FnMut?
Because they call the closure multiple times, and allowing mutation makes the API more useful for counters, accumulation, and local state.
Should I use Fn, FnMut, or FnOnce in my own function signatures?
Use the least restrictive bound that matches your function:
- one call:
FnOnce - repeated calls with mutation allowed:
FnMut - repeated calls with read-only behavior:
Fn
Can a closure that captures nothing still implement all three traits?
Yes. A closure that captures nothing can implement Fn, FnMut, and FnOnce.
Mini Project
Description
Build a small Rust program that demonstrates all three closure traits side by side. This helps you see how reading, mutating, and consuming captured values affect which trait a closure can satisfy.
Goal
Create three closures: one that is Fn, one that is FnMut, and one that is FnOnce, and pass each to a function with the appropriate trait bound.
Requirements
- Write one function that accepts
Fn(), one that acceptsFnMut(), and one that acceptsFnOnce(). - Create three closures showing read-only access, mutable access, and consuming ownership.
- Call each closure through the correct function.
- Make sure the program compiles and prints output that shows the difference in behavior.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.