← Fundamentals
Guided lesson 7 questions · Rust only

Rust closure capture and Fn traits

Read a closure’s body to know what it borrows, what it changes, and what it takes.

A Rust closure captures each variable in the least demanding way its body allows: a shared borrow to read it, a mutable borrow to change it, or the value itself to move it out. What the body does decides the closure’s traits: Fn, FnMut, or FnOnce. move makes the closure take every value, and a Copy value is then copied.

Written for Rust.

Before you begin

Why this matters

Closures are passed to many standard functions and returned from your own, and the compiler errors they cause name Fn, FnMut, and FnOnce. If you can read from a closure’s body how it captures each variable, you can predict those errors, and the bug that compiles: a move closure that counts on its own copy.

What this helps you reason about

  • Say from a closure’s body whether it reads, changes, or gives away each variable, and which of Fn, FnMut, and FnOnce it implements.
  • Predict what a move closure does to a counter, and spot the copy that loses updates.
  • Choose the bound a function should ask for, and lend or return a closure.

In JavaScript, Python, or Go, you can call a function value as often as you like, and it uses the variables themselves, not copies. A Rust closure is more specific. For each variable it uses, the compiler decides whether the closure borrows it, borrows it to change it, or takes it. That decision fixes what the closure may do: be called any number of times, be called only through a mut binding, or be called once. This lesson reads those decisions from the closure’s body and follows them into function signatures.

Already know this? The short version
  • A closure captures each variable by shared borrow if its body only reads it, by mutable borrow if it changes it, and by value if it moves it out; move makes every capture by value, copying Copy values.
  • The body decides the traits: Fn (reads its captures), FnMut (changes them; the binding needs let mut, E0596), FnOnce (moves one out; a second call is E0382). Every Fn is FnMut, and every closure is FnOnce.
  • A move closure that counts changes its own copy of a Copy counter, and the outer variable never changes.
  • Ask for the weakest bound the function body needs: FnOnce if it calls once, FnMut if it calls several times, Fn if it calls through a shared reference; pass &mut f to keep using a closure. Return one closure type as impl Fn(…), and different closures as Box<dyn Fn(…)>.
  • The section “What goes wrong with closures in real programs?” covers a String moved out of a closure, an API that needs FnMut (E0525), move copying a counter, and a closure that holds a borrow (E0499, E0502).

Go to the final check· Go to the practice tickets

Words in this lesson (8)
closure
A function without a name, written |parameters| body, together with the variables from the surrounding code that it uses. It borrows those variables, or takes them when written with move.
capture
To use a variable from the surrounding code inside a closure. A Rust closure captures by borrowing, or, with move, by taking the value: a copy for a Copy type, a move for the others.
mutable reference
A reference written &mut x, of type &mut T, which can change x. While it is in use, x cannot be used in any other way, not even by its owner.
trait
A named set of abilities that a type can have. A type that has a trait is said to implement it.
Fn, FnMut, and FnOnce
The three traits that say how a closure may be called. Every closure implements FnOnce: it can be called at least once. A closure that does not move a captured value out also implements FnMut, and one that does not change its captures also implements Fn.
Copy
A trait (a named set of abilities a type can have) for types whose bytes are a complete value, such as i32 or bool. Assigning a Copy value copies it, and the old binding stays usable.
trait bound
A requirement on a type parameter, such as T: Vehicle in fn farthest<T: Vehicle>(a: T, b: T) -> T. T is a type chosen by each call, and it must implement Vehicle.
Box
A small value that owns one value stored on the heap. Box::new(3) puts 3 on the heap and returns the Box that owns it.

Examples are focused snippets; imports, enclosing functions, or classes may be omitted. C examples use C11; Haskell examples use Haskell 2010. Expand the notes for language-specific depth.

01 / Capture

How does a closure capture each variable?

Closures and captured variables showed that a Rust closure borrows each variable it uses, or takes it when written with move. This lesson looks at which borrow, and what that allows.

Without move, the compiler reads the closure’s body and chooses, for each variable, the least the body needs. If the body only reads the variable, the closure holds a shared borrow. If the body changes it, the closure holds a mutable reference. If the body moves the value out, the closure takes the value; the next section shows that case.

The choice decides which traits the closure implements. A trait is a named set of abilities that a type can have. A closure that only reads its captures implements Fn: it can be called any number of times. A closure that changes a capture implements FnMut. Calling it changes the closure’s own state, so the binding that holds it must be let mut. Without mut, the compiler reports:

error[E0596]: cannot borrow `enter` as mutable, as it is not declared as mutable

with “calling `enter` requires mutable binding due to mutable borrow of `visitors`” under the closure.

main.rs
let closing = 18;
let mut visitors = 0;
// reads closing: a shared borrow, Fn
let show = || println!("Open until {}", closing);
// changes visitors: a mutable borrow, FnMut
let mut enter = || visitors += 1;
show();
enter();
enter();
println!("{} visitors", visitors); // 2 visitors

A closure borrows what it reads and borrows mutably what it changes; a closure that changes a capture needs let mut.

Coming from C++: A C++ lambda lists its captures, as [&] or [=]. A Rust closure chooses for each variable from its body, and move plays the part of [=].

Precisely: a closure is a struct

The compiler turns each closure into an unnamed struct with one field per capture, plus a call method. That is why the stepper shows enter as {_ref__visitors: 0x… → 0}: one field, a reference. The traits are implemented by the compiler, never written by hand. FnMut’s call method takes &mut self, which is why calling it needs a mut binding; Fn’s takes &self, and FnOnce’s takes self, the closure itself.

Experiment

Watch one closure read and another change

show only reads closing, so it holds a shared borrow of closing and implements Fn. enter changes visitors, so it holds a mutable borrow of visitors and implements FnMut. Calling a FnMut closure changes the closure’s own state, so the binding that holds it needs mut, like any value that is changed.

Why it matters Read a closure’s body to know how it captures each variable. A closure that changes a capture needs let mut.

Predict the three output lines. Then change let mut enter to let enter, and read the compiler error.

fn main() {
    let closing = 18;
    let mut visitors = 0;
    let show = || println!("Open until {}", closing);
    let mut enter = || visitors += 1;
    show();
    enter();
    enter();
    show();
    println!("{} visitors", visitors);
}
What to look for

At the stop on line 6, main lists show as {_ref__closing: 0x… → 18} and enter as {_ref__visitors: 0x… → 0}. The stepper names a borrowed capture _ref__ followed by the variable’s name, and shows the address the closure holds, an arrow, and the value at that address. Step into the calls: the frame list names show main::main::{closure#0} and enter main::main::{closure#1}, numbered in the order the closures appear in main. After each call to enter, main’s visitors and the value after enter’s arrow change together, to 1 and then 2. The run prints Open until 18 twice, then 2 visitors. After the edit, the compiler reports error[E0596]: cannot borrow `enter` as mutable, as it is not declared as mutable, with “calling `enter` requires mutable binding due to mutable borrow of `visitors`” under the closure.

Expected output

Open until 18
Open until 18
2 visitors

02 / FnOnce

Why can some closures be called only once?

Some closures move a captured value out: they return it, push it into a Vec, or pass it to a function that takes ownership. The closure must own the value to give it away, so it captures that variable by value, even without move.

After the first call, the value is gone from the closure. So the closure implements only FnOnce, and calling it moves the closure itself. A second call is rejected:

error[E0382]: use of moved value: `hand_over`

with “`hand_over` moved due to this call” under the first call, and the note “closure cannot be invoked more than once because it moves the variable `notice` out of its environment”.

If the closure must run more than once, do not give the captured value away. Give away a clone, || notice.clone(), at the cost of copying the text each time. Or let the code that runs inside the closure borrow the value, as println!("{}", notice) does.

main.rs
let notice = String::from("Closing in 10 minutes");
// returns notice: moves it out, FnOnce
let hand_over = || notice;
let posted = hand_over();
// a second call: error[E0382]
// let again = hand_over();
println!("{}", posted);

A closure that moves a captured value out implements only FnOnce and can be called once.

Precisely: what counts as moving out

Only the body decides. || notice, || drop(notice), and || list.push(notice) move notice out. || notice.len() and || println!("{}", notice) only read it, so the closure stays Fn. A Copy value can never be moved out in this sense: || closing returns a copy, and the closure stays Fn.

Experiment

Hand over a String from a closure

The body returns notice, which moves the String out of the closure. So the closure captures notice by value and implements only FnOnce. Calling a FnOnce closure moves the closure itself into the call, and nothing is left for a second call.

Why it matters A closure that gives away a captured value can run once. If it must run again, give away a clone, or let the body borrow the value.

Predict what is printed. Then add the line let again = hand_over(); below line 4, and read the compiler error.

fn main() {
    let notice = String::from("Closing in 10 minutes");
    let hand_over = || notice;
    let posted = hand_over();
    println!("{}", posted);
}
What to look for

At the stop on line 4, hand_over shows {notice: "Closing in 10 minutes" (len=21, cap=21, data=0x…)}. There is no _ref__, so the closure holds the String itself, with the same data= address as main’s notice. main still lists notice, and after the call hand_over still shows the text too: the stepper shows the bytes left behind by a move, and the compiler rejects any use of them. After the call, posted shows the same data= address: the text was never copied, it got a new owner. The run prints Closing in 10 minutes. After the edit, the compiler reports error[E0382]: use of moved value: `hand_over`, with “`hand_over` moved due to this call” under the first call and the note “closure cannot be invoked more than once because it moves the variable `notice` out of its environment”.

Expected output

Closing in 10 minutes

03 / move

What does move change, and what does it not change?

move tells the compiler to capture every variable by value, whatever the body does with it. For a String, that is a move into the closure. For a Copy value, such as an i32, the closure gets its own copy when it is made. Closures and captured variables used move for a closure returned from a function, which must not borrow that function’s locals (E0373).

move does not change which trait the closure implements. That is still decided by the body: a move closure that only reads is Fn, and one that changes a capture is FnMut, because it changes its own copy.

That is the trap. A move closure that counts visitors changes its own copy of the counter. The variable outside never changes, and the program compiles without an error.

main.rs
let mut visitors = 0;
let mut enter = move || {
    // changes the closure's own copy
    visitors += 1;
    visitors
};
enter();
let last = enter();
println!("{} {}", last, visitors); // 2 0

move captures by value and copies Copy values; it does not change the trait, and a counter inside a move closure is the closure’s own.

Coming from JavaScript: A JavaScript closure that counts changes the outer variable. A Rust move closure changes its own copy, and the outer variable keeps its old value.

Precisely: borrowing inside a move closure

To move one value into a closure and borrow another, make a reference first, let sign = &notice;, and capture sign with move: the closure then owns a copy of the reference, not the String.

Experiment

Count visitors in a move closure

move makes the closure capture every variable by value. visitors is an i32, which is Copy, so the closure gets its own copy when it is made. visitors += 1 changes that copy, so the closure is still FnMut, but main’s visitors never changes.

Why it matters move does not make a closure share a counter. It gives the closure its own copy, and the variable outside keeps its old value.

Predict both numbers. Then remove the word move from line 3 and predict again.

fn main() {
    let mut visitors = 0;
    let mut enter = move || {
        visitors += 1;
        visitors
    };
    enter();
    let last = enter();
    println!("{} {}", last, visitors);
}
What to look for

The stepper does not stop on a closure’s lines until the closure is called, so the stop after line 3 is line 7. There, enter shows {visitors: 0}: no _ref__, so the closure holds its own number. Step into each call: the closure’s frame lists its visitors, 1 and then 2, and enter in main shows the same. main’s visitors stays 0. The run prints 2 0. After the edit, enter shows {_ref__visitors: 0x… → 0}, main’s visitors changes with each call, and the run prints 2 2.

Expected output

2 0

04 / Bounds

Which closures can a function accept?

A function can take a closure as a parameter. fn twice<F: FnMut()>(mut action: F) has a type parameter F: a name for a type chosen by each call, here the type of the closure. F: FnMut() is a trait bound: the closure must implement FnMut, take no arguments, and return nothing. fn twice(mut action: impl FnMut()) means the same in a shorter form.

The three traits fit inside each other. Every Fn closure is also FnMut, and every closure is FnOnce. So the bound decides two things: which closures callers may pass, and what the function may do with the closure.

A FnOnce bound accepts every closure, but the function may call it only once. A FnMut bound accepts every closure except one that moves a capture out, and the function may call it many times, one call after another. A Fn bound accepts only closures that do not change their captures, and the function may call it through a shared reference, or from several places at once.

Ask for the weakest bound your function’s body needs. If the bound is Fn and the caller’s closure changes a capture, the compiler reports error[E0594]: cannot assign to `total`, as it is a captured variable in a `Fn` closure, and suggests FnMut instead.

Passing a closure to a function moves it, like any value that is not Copy. To keep using it, pass &mut sell: a mutable reference to a FnMut closure is itself FnMut. Without the &mut, a second twice(sell) gives error[E0382]: use of moved value: `sell`, and the compiler’s help suggests &mut sell.

main.rs
fn twice<F: FnMut()>(mut action: F) {
    action();
    action();
}

let mut tickets = 0;
let mut sell = || tickets += 1;
twice(&mut sell); // lend the closure
twice(&mut sell);
println!("{} tickets", tickets); // 4 tickets

Every Fn closure is FnMut, and every closure is FnOnce. Ask for the weakest bound the function body needs, and lend a closure with &mut to keep it.

Coming from Java: Java has one interface per shape, such as Runnable or Supplier. Rust has three traits for each shape, and the closure’s body decides which ones it implements.

Precisely: plain functions and other references

A named function, such as fn half(price: u32) -> u32, implements all three traits, so it can be passed wherever a closure is accepted. A shared reference to a Fn closure, &show, is Fn too. Each call to twice with a different closure type compiles a separate copy of twice for that type, so a generic bound costs nothing when the program runs.

Experiment

Lend a closure to a function twice

twice calls action two times, so it needs at least FnMut. &mut sell lends the closure for one call to twice: a mutable reference to a FnMut closure is itself FnMut. With FnOnce, the first call moves action into it, and the second call has nothing to call.

Why it matters Choose the bound from what the function’s body does with the closure. Pass &mut to lend a closure that you will call again.

Predict the number of tickets. Then change F: FnMut() to F: FnOnce() on line 1, and read the compiler error.

fn twice<F: FnMut()>(mut action: F) {
    action();
    action();
}

fn main() {
    let mut tickets = 0;
    let mut sell = || tickets += 1;
    twice(&mut sell);
    twice(&mut sell);
    println!("{} tickets", tickets);
}
What to look for

Step into twice. The frame list names it main::twice<&mut main::main::{closure_env#0}>: the part in angle brackets is the type chosen for F, a mutable reference to the closure. action shows an address, then → {_ref__tickets: 0x…}. Each action() enters the closure, and main’s tickets goes 1, 2, 3, 4. The run prints 4 tickets. After the edit, the compiler reports error[E0382]: use of moved value: `action`, with “`action` moved due to this call” under the first action() and the note “this value implements `FnOnce`, which causes it to be moved when called”. Its help line suggests FnOnce() + Copy; the bound this body needs is FnMut.

Expected output

4 tickets

05 / Returning

How do you return a closure?

Each closure has its own type, which has no name you can write. To return a closure, write impl Fn(u32) -> u32 as the return type: “one type that implements Fn(u32) -> u32”. Closures and captured variables returned impl FnMut() -> u32 the same way. A returned closure must own what it captures, so it uses move.

impl Fn stands for one type. A function that returns one closure in one branch and a different closure in another is rejected:

error[E0308]: `if` and `else` have incompatible types

with the note “no two closures, even if identical, have the same type” and the help “consider boxing your closure and/or using it as a trait object”.

The fix is Box<dyn Fn(u32) -> u32>. A Box is a small value that owns one value stored on the heap. dyn Fn(u32) -> u32 means “some type that implements Fn(u32) -> u32”, chosen while the program runs. Each branch puts its own closure in a Box, and both branches return the same type. Each call then looks up the closure’s code through the Box, which costs a little more than a call through impl Fn.

main.rs
fn price_rule(member: bool, fee: u32)
    -> Box<dyn Fn(u32) -> u32> {
    if member {
        Box::new(|price| price / 2)
    } else {
        Box::new(move |price| price + fee)
    }
}

let guest = price_rule(false, 100);
println!("{}", guest(800)); // 900

Return one closure as impl Fn(…); return different closures as Box<dyn Fn(…)>.

Coming from Go: In Go, every func(int) int has the same type, so two branches can return two function literals. In Rust, each closure has its own type, so they need a Box<dyn Fn(…)>.

Precisely: closures that capture nothing

A closure that captures nothing can become a plain function pointer, written fn(u32) -> u32. So an if and else that return two such closures compile even with impl Fn: both become the same pointer type. The error appears as soon as one of them captures a variable, such as fee. A Box of a closure that captures nothing allocates no heap memory, because the closure has no bytes; the stepper shows its pointer as 0x1.

06 / In production

What goes wrong with closures in real programs?

Four mistakes with closures are common in real code.

A closure that moves a String out, where it must run again. A retry loop around || send(report), where send takes a String, fails at the second call with E0382 and the note “closure cannot be invoked more than once because it moves the variable `report` out of its environment”. Pass report.clone(), or change send to take &str.

An API that needs FnMut. A function that calls your closure many times asks for FnMut or Fn. Vec’s retain and sort_by_key ask for FnMut, for example. A closure that moves a capture out is rejected where it is passed: error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`, with “closure is `FnOnce` because it moves the variable `notice` out of its environment”.

move copying a counter. move is often added to fix E0373, for a closure that is returned or stored. If the closure also counts, it counts on its own copy, and the outer counter stays at its old value. Read the count from the closure, for example as its return value.

A closure that holds a borrow blocks other code. From where a closure that changes visitors is made to its last call, it holds a mutable reference to visitors. Reading visitors between the calls gives error[E0502]: cannot borrow `visitors` as immutable because it is also borrowed as mutable. A second closure that changes it gives error[E0499]: cannot borrow `visitors` as mutable more than once at a time. Finish the calls first, or pass the value in as a parameter.

main.rs
let mut sold = 0;
// captures nothing: sold is passed in
let sell = |total: &mut u32, tickets: u32| *total += tickets;
sell(&mut sold, 3);
println!("sold {}", sold); // sold 3
sell(&mut sold, 2);

Give a closure that must run again a clone, ask for the bound the API needs, read a count from the closure, and pass in state that other code also uses.

Experiment

Pass the count in instead of capturing it

A closure that changes sold holds a mutable borrow of sold from where it is made to its last call. A parameter is borrowed only for one call. So the closure that takes &mut u32 leaves sold free between the calls.

Why it matters When other code must use a value between calls, pass the value to the closure as a parameter instead of capturing it.

Predict both output lines. Then replace line 3 with let mut sell = |total: &mut u32, tickets: u32| sold += tickets; so that the closure changes sold directly, and read the compiler errors.

fn main() {
    let mut sold = 0;
    let sell = |total: &mut u32, tickets: u32| *total += tickets;
    sell(&mut sold, 3);
    println!("sold {}", sold);
    sell(&mut sold, 2);
    println!("sold {}", sold);
}
What to look for

sell shows {}: the closure captures nothing. Step into each call: total shows an address, then → 0 in the first call and → 3 in the second. It is the address of main’s sold, lent only for that call. Between the calls, main reads sold, and the run prints sold 3, then sold 5. After the edit, the compiler reports error[E0499]: cannot borrow `sold` as mutable more than once at a time at sell(&mut sold, 3), with “first mutable borrow occurs here” under the closure and “first borrow later used here” under the second call. It also reports error[E0502]: cannot borrow `sold` as immutable because it is also borrowed as mutable, at sold in the first println!.

Expected output

sold 3
sold 5

07 / Try it

Can you tell a copy from a borrow?

run_twice calls a FnMut closure twice and returns the second result. It is called with a move closure and then with a closure that borrows.

main.rs
fn run_twice<F: FnMut() -> u32>(mut f: F) -> u32 {
    f();
    f()
}

fn main() {
    let mut count = 10;
    let copied = run_twice(move || {
        count += 1;
        count
    });
    let borrowed = run_twice(|| {
        count += 5;
        count
    });
    println!("{} {} {}", copied, borrowed, count);
}

What is printed? Could either closure be passed to a function whose bound is Fn() -> u32?

Reveal the reasoning

12 20 20. The move closure gets its own copy of count, 10, and returns 11 and then 12, while main’s count stays 10. The second closure borrows count mutably, so it adds 5 twice to main’s count, which becomes 20, and returns 20. Neither closure could be passed where Fn is required: both change a capture, so both are FnMut. The compiler would report E0594.

Practice tickets

  1. RUST-501 · The library van’s screen shows every book still on board → Free
  2. RUST-502 · Only the first visitor’s badge names the tour → Sign in · Pro
  3. RUST-503 · Every group booking for the author talk starts at seat 1 → Sign in · Pro

Keep exploring

Arrays, lists, and vectors →

Primary references