Skip to content

Rust Fn, FnMut & FnOnce

On the Closures page you learned how to write a closure, how it captures variables, and how it can change them. This page answers the next question: how do you write your own function that accepts a closure? And how do you hand a closure back from a function?

The answer is three small traits: Fn, FnMut and FnOnce. Think of them like the rules at a library. Some visitors only read the books (Fn). Some write notes in the margins (FnMut). And some take a book home and never bring it back (FnOnce). Rust looks at what your closure does, and gives it the matching label. If the traits look scary at first, don’t worry. There are only three, and the compiler picks the right one for you.

Every closure has its own unique type, made up by the compiler. You can’t write that type’s name. Even two closures that look exactly the same have different types:

fn main() {
let mut rules = vec![|price: u32| price * 2];
rules.push(|price: u32| price * 2);
}
error[E0308]: mismatched types
--> src/main.rs:3:16
|
3 | rules.push(|price: u32| price * 2);
| ---- ^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
|
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object

So a function can’t say “give me a closure of type X”. Instead, it says “give me any type that can be called like this”. That’s a trait bound, just like the ones on the Generics and Traits pages:

fn price_after<F: Fn(u32) -> u32>(rule: F, price: u32) -> u32 {
rule(price)
}
fn main() {
let coupon = 15;
let half_price = |price: u32| price / 2;
let use_coupon = |price: u32| price - coupon;
println!("{}", price_after(half_price, 80));
println!("{}", price_after(use_coupon, 80));
}

Output:

40
65
▶ Try it Yourself

Read F: Fn(u32) -> u32 as: “F is some type you can call with a u32, and it gives back a u32.” Both closures fit, even though their types are different.

The compiler looks at what the closure’s body does with the values it captured:

  • Fn: it only reads them. You can call it as many times as you like.
  • FnMut: it changes them. You can call it many times, but it needs mut.
  • FnOnce: it gives them away (moves them out). You can call it only once, because after that the value is gone.
fn main() {
let shop = String::from("Corner Bakery");
// Fn: only reads `shop`
let greet = || println!("Welcome to {shop}!");
greet();
greet();
// FnMut: changes `orders`
let mut orders = 0;
let mut take_order = || {
orders += 1;
println!("order #{orders} taken");
};
take_order();
take_order();
// FnOnce: hands `receipt` back to the caller
let receipt = String::from("2 croissants, 1 coffee");
let hand_over = || receipt;
let customer_copy = hand_over();
println!("customer has: {customer_copy}");
}

Output:

Welcome to Corner Bakery!
Welcome to Corner Bakery!
order #1 taken
order #2 taken
customer has: 2 croissants, 1 coffee
▶ Try it Yourself

hand_over returns receipt itself, not a copy. Once the receipt is handed over, the closure has nothing left to give. That’s why it’s FnOnce.

Here’s the picture to keep in your head. Imagine three circles, one inside the other:

  • The big outer circle is FnOnce. Every closure can be called at least once, so every closure is in here.
  • Inside it is a smaller circle, FnMut: closures that can be called again and again.
  • In the very middle is Fn: closures that can be called again and again without changing anything.

So every Fn closure is also an FnMut and an FnOnce. This matters when you write a function. A parameter that asks for FnOnce accepts any closure. A parameter that asks for Fn is the pickiest one.

One thing that surprises people: the move keyword does not pick the trait. move || println!("{shop}") takes ownership of shop, but it only reads it, so it’s still Fn. Only the body decides.

There are three ways to write the same bound. They all mean the same thing, so pick whichever reads best:

// 1. A generic parameter with a bound
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(x)
}
// 2. `impl Fn` in argument position: shorter, no name needed
fn apply_twice(f: impl Fn(i32) -> i32, x: i32) -> i32 {
f(f(x))
}
// 3. A `where` clause: tidy when the bound gets long
fn apply_to_all<F>(f: F, scores: &[i32]) -> Vec<i32>
where
F: Fn(i32) -> i32,
{
scores.iter().map(|&s| f(s)).collect()
}
fn main() {
let bonus = 10;
let add_bonus = |score: i32| score + bonus;
println!("{}", apply(add_bonus, 50));
println!("{}", apply_twice(add_bonus, 50));
println!("{:?}", apply_to_all(add_bonus, &[40, 65, 90]));
}

Output:

60
70
[50, 75, 100]
▶ Try it Yourself

You might wonder how we could pass add_bonus three times. A closure that only reads its captures is cheap to copy, so Rust copies it each time.

Now FnMut and FnOnce. With FnMut, the parameter needs mut, because calling the closure changes what’s inside it. With FnOnce, you can call it once, and that’s it:

fn process_tasks<F: FnMut(&str)>(tasks: &[&str], mut on_done: F) {
for task in tasks {
on_done(task);
}
}
fn on_shutdown<F: FnOnce()>(cleanup: F) {
println!("shutting down...");
cleanup();
}
fn main() {
let mut finished = Vec::new();
process_tasks(&["send emails", "back up database"], |task| {
finished.push(task.to_string());
});
println!("finished: {:?}", finished);
on_shutdown(|| {
// This loop takes ownership of `finished`, so the closure is FnOnce
for task in finished {
println!("archived: {task}");
}
});
}

Output:

finished: ["send emails", "back up database"]
shutting down...
archived: send emails
archived: back up database
▶ Try it Yourself

A good rule of thumb: ask for the loosest trait your function can live with. If you call the closure only once, ask for FnOnce, and callers can pass anything. You’ll see this in the standard library too: Option::map takes FnOnce, while Iterator::map takes FnMut.

The compiler catches every mismatch, and its messages are very friendly here. Say a function asks for Fn, but your closure changes a counter:

fn run_report<F: Fn()>(f: F) {
f();
f();
}
fn main() {
let mut runs = 0;
run_report(|| {
runs += 1;
println!("report run {runs}");
});
}
error[E0594]: cannot assign to `runs`, as it is a captured variable in a `Fn` closure
--> src/main.rs:9:9
|
1 | fn run_report<F: Fn()>(f: F) {
| - change this to accept `FnMut` instead of `Fn`
...
8 | run_report(|| {
| ---------- -- in this closure
| |
| expects `Fn` instead of `FnMut`
9 | runs += 1;
| ^^^^^^^^^ cannot assign

It even tells you the fix: accept FnMut, and add mut to the parameter. With that change, the program prints report run 1 and report run 2:

fn run_report<F: FnMut()>(mut f: F) {
f();
f();
}

The other classic mistake is calling an FnOnce closure twice:

fn main() {
let report = String::from("Q3 sales");
let send = || {
let sent = report;
println!("sending {sent}");
};
send();
send();
}
error[E0382]: use of moved value: `send`
--> src/main.rs:8:5
|
7 | send();
| ------ `send` moved due to this call
8 | send();
| ^^^^ value used here after move
|
note: closure cannot be invoked more than once because it moves the variable `report` out of its environment
--> src/main.rs:4:20
|
4 | let sent = report;
| ^^^^^^

The note says it plainly: the first call gave report away, so there’s nothing left for a second call. If you really need two calls, borrow instead (let sent = &report;) and the closure becomes Fn. If you pass a closure like this to a function that asks for Fn, you get a similar message: error[E0507]: cannot move out of report, a captured variable in an Fn closure.

A function can also build a closure and hand it back. You write the return type as impl Fn(i32) -> i32, which means “some closure type; you don’t need to know its name”:

fn make_adder(amount: i32) -> impl Fn(i32) -> i32 {
move |x| x + amount
}
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor
}
fn main() {
let add_tip = make_adder(5);
let triple = make_multiplier(3);
println!("{}", add_tip(20));
println!("{}", triple(7));
println!("{}", add_tip(triple(10)));
}

Output:

25
21
35
▶ Try it Yourself

Why the move? Without it, the closure would only borrow amount. But amount belongs to make_adder, and it disappears the moment make_adder returns. The closure would be holding a reference to nothing. Rust refuses:

error[E0373]: closure may outlive the current function, but it borrows `amount`, which is owned by the current function
--> src/main.rs:2:5
|
2 | |x| x + amount
| ^^^ ------ `amount` is borrowed here
| |
| may outlive borrowed value `amount`
|
help: to force the closure to take ownership of `amount` (and any other referenced variables), use the `move` keyword
|
2 | move |x| x + amount
| ++++

With move, the closure packs its own copy of amount and carries it along, like taking your lunch with you instead of leaving it in someone else’s fridge.

impl Fn stands for one hidden type. So you can’t return one closure from the if branch and a different one from the else branch. You also can’t put different closures in the same Vec. For both jobs, put each closure in a box: Box<dyn Fn(f64) -> f64> means “any closure with this shape, stored on the heap”. It’s the same idea as the Trait Objects page.

Here’s a checkout that applies a list of named price rules, in order:

fn shipping_rule(express: bool, fee: f64) -> Box<dyn Fn(f64) -> f64> {
if express {
Box::new(move |total| total + fee * 2.0)
} else {
Box::new(move |total| total + fee)
}
}
fn main() {
let member_percent = 10.0;
let coupon = 5.0;
let rules: Vec<(&str, Box<dyn Fn(f64) -> f64>)> = vec![
("member discount", Box::new(move |total: f64| total * (1.0 - member_percent / 100.0))),
("coupon", Box::new(move |total: f64| total - coupon)),
("standard shipping", shipping_rule(false, 3.5)),
];
let mut total = 80.0;
println!("start: {total:.2}");
for (name, rule) in &rules {
total = rule(total);
println!("after {name}: {total:.2}");
}
}

Output:

start: 80.00
after member discount: 72.00
after coupon: 67.00
after standard shipping: 70.50
▶ Try it Yourself

Boxing costs a tiny heap allocation, and each call goes through a pointer. That’s almost never a problem. Use impl Fn when there’s one closure type, and Box<dyn Fn> when there are many.

A struct can hold a closure too. Since you can’t name the closure’s type, you make the struct generic over it. Here’s a small button that runs some code when clicked:

struct Button<F: Fn()> {
label: String,
on_click: F,
}
impl<F: Fn()> Button<F> {
fn click(&self) {
println!("[{}] clicked", self.label);
(self.on_click)();
}
}
fn main() {
let user = String::from("Ada");
let save = Button {
label: String::from("Save"),
on_click: move || println!("saving {user}'s draft"),
};
save.click();
save.click();
}

Output:

[Save] clicked
saving Ada's draft
[Save] clicked
saving Ada's draft
▶ Try it Yourself

Notice the parentheses in (self.on_click)(). They tell Rust “take the field, then call it”. If the button needs to change its captures on each click, use F: FnMut() and make the method take &mut self.

Regular functions can be passed around too. Their type is written with a small fn, like fn(i32) -> i32. This is a function pointer, a plain address of some code. A closure that captures nothing can turn into one as well:

fn double(x: i32) -> i32 {
x * 2
}
fn square(x: i32) -> i32 {
x * x
}
fn apply_op(op: fn(i32) -> i32, x: i32) -> i32 {
op(x)
}
fn main() {
println!("{}", apply_op(double, 4));
println!("{}", apply_op(|x| x - 1, 4));
let steps: [fn(i32) -> i32; 3] = [double, square, |x| x + 1];
let mut value = 3;
for step in steps {
value = step(value);
}
println!("{value}");
}

Output:

8
3
37
▶ Try it Yourself

Function pointers all share one type, so you can put them in an array with no boxing. The catch is that a closure which captures something can’t become a function pointer. The compiler says: closures can only be coerced to fn types if they do not capture any variables. The good news goes the other way: every regular function also implements Fn, FnMut and FnOnce, so you can pass double to apply from earlier. That’s why most code uses the closure traits: they accept both.

Forgetting mut on an FnMut parameter. Calling an FnMut changes it, so its binding must be mut:

error[E0596]: cannot borrow `on_done` as mutable, as it is not declared as mutable
--> src/main.rs:3:9
|
3 | on_done(task);
| ^^^^^^^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
1 | fn process_tasks<F: FnMut(&str)>(tasks: &[&str], mut on_done: F) {
| +++

Returning different closures from -> impl Fn. Two closures are two types, even if they look alike:

fn shipping_rule(express: bool, fee: f64) -> impl Fn(f64) -> f64 {
if express {
move |total| total + fee * 2.0
} else {
move |total| total + fee
}
}
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:5:9
|
2 | / if express {
3 | | move |total| total + fee * 2.0
| | ------------------------------
| | |
| | the expected closure
| | expected because of this
4 | | } else {
5 | | move |total| total + fee
| | ^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
6 | | }
| |_____- `if` and `else` have incompatible types
|
= note: expected closure `{closure@src/main.rs:3:9: 3:21}`
found closure `{closure@src/main.rs:5:9: 5:21}`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
help: you could change the return type to be a boxed trait object
|
1 - fn shipping_rule(express: bool, fee: f64) -> impl Fn(f64) -> f64 {
1 + fn shipping_rule(express: bool, fee: f64) -> Box<dyn Fn(f64) -> f64> {
|
help: if you change the return type to expect trait objects, box the returned expressions
|
3 ~ Box::new(move |total| total + fee * 2.0)
4 | } else {
5 ~ Box::new(move |total| total + fee)
|

Do what the compiler suggests: return Box<dyn Fn(f64) -> f64> and wrap each closure in Box::new(...).

Calling a closure field like a method. Writing self.on_click() makes Rust look for a method named on_click:

error[E0599]: no method named `on_click` found for reference `&Button<F>` in the current scope
--> src/main.rs:9:14
|
9 | self.on_click();
| ^^^^^^^^ field, not a method
|
help: to call the type parameter stored in `on_click`, surround the field access with parentheses
|
9 | (self.on_click)();
| + +

Write a function ticket_dispenser(start: u32) that returns a closure. Each call to the closure gives the next ticket number: start, then start + 1, and so on. Make two dispensers (a bakery starting at 100, a pharmacy starting at 1) and show that they count separately. Hint: the closure changes its own counter, so the return type is impl FnMut() -> u32, and you’ll need move.

Show solution
fn ticket_dispenser(start: u32) -> impl FnMut() -> u32 {
let mut next = start;
move || {
let ticket = next;
next += 1;
ticket
}
}
fn main() {
let mut bakery = ticket_dispenser(100);
let mut pharmacy = ticket_dispenser(1);
println!("bakery: {}", bakery());
println!("bakery: {}", bakery());
println!("pharmacy: {}", pharmacy());
println!("bakery: {}", bakery());
}

Output:

bakery: 100
bakery: 101
pharmacy: 1
bakery: 102
▶ Try it Yourself

Each dispenser moved its own next inside, so they never share a counter. The variables bakery and pharmacy need mut because they are FnMut.

Write retry(max_attempts, task). It calls task until it returns Ok, or until it has tried max_attempts times. Print a line for each failed attempt. Test it with a closure that fails twice with "server busy", then succeeds. Hint: the test closure counts its own calls, so the bound is FnMut() -> Result<String, String>, and the parameter needs mut.

Show solution
fn retry<F>(max_attempts: u32, mut task: F) -> Result<String, String>
where
F: FnMut() -> Result<String, String>,
{
for attempt in 1..=max_attempts {
match task() {
Ok(value) => return Ok(value),
Err(e) => println!("attempt {attempt} failed: {e}"),
}
}
Err(String::from("gave up"))
}
fn main() {
let mut calls = 0;
let result = retry(5, || {
calls += 1;
if calls < 3 {
Err(String::from("server busy"))
} else {
Ok(String::from("data downloaded"))
}
});
println!("{:?} after {calls} calls", result);
}

Output:

attempt 1 failed: server busy
attempt 2 failed: server busy
Ok("data downloaded") after 3 calls
▶ Try it Yourself

retry calls the task more than once, so FnOnce won’t do, and the task changes calls, so Fn won’t do either. FnMut is exactly right. When retry returns, the closure is done, so main can read calls again.

  • Every closure has its own unique type, so functions accept closures through a trait bound like F: Fn(i32) -> i32.
  • Fn only reads its captures, FnMut changes them, and FnOnce moves them out and can be called once. The body decides, not move.
  • Every Fn is also FnMut and FnOnce. Ask for the loosest trait your function needs.
  • Write bounds as <F: Fn(..)>, impl Fn(..), or in a where clause. FnMut parameters need mut.
  • Return one closure with -> impl Fn(..) (usually with move). Use Box<dyn Fn(..)> for different closures from branches or in a Vec.
  • fn(i32) -> i32 is a function pointer. Regular functions and non-capturing closures fit it.

Quick check

1. A closure does total += price on a captured variable. Which is the strictest trait it implements?

2. Your function calls its closure parameter exactly once. Which bound accepts the most closures?

3. Why does make_adder(amount: i32) -> impl Fn(i32) -> i32 need move |x| x + amount?

4. You want a Vec holding several different discount closures. What type do the elements need?

Score: 0 / 4