What Is the Difference Between Fn, FnMut, and FnOnce?

FnOnce runs once and consumes captures, FnMut runs multiple times and mutates captures, and Fn runs multiple times without mutating captures.

When the compiler argues about your closure

You write a function that accepts a callback. You pass a closure that moves a variable. The compiler rejects it. You change the trait bound from Fn to FnOnce. It compiles. You change it to FnMut. It rejects it again.

Rust is not being arbitrary. The traits Fn, FnMut, and FnOnce describe exactly what a closure does to the variables it captures. They are promises about ownership and borrowing. The compiler uses these promises to guarantee memory safety without a garbage collector.

Understanding these traits is essential for writing flexible APIs and for debugging the closure errors that trip up every Rust beginner. The trait name tells you how the closure interacts with its environment.

The three capture modes

A closure is an anonymous function that captures variables from the surrounding scope. When you write || x + 1, the compiler looks at x and decides how to capture it. The capture mode determines which trait the closure implements.

Fn means the closure captures by immutable reference. It reads the variables. It can be called multiple times. The variables remain usable after the closure runs.

FnMut means the closure captures by mutable reference. It modifies the variables. It can be called multiple times. The variables are mutated, but they are not moved.

FnOnce means the closure captures by move. It takes ownership of the variables. It can be called exactly once. After the call, the captured variables are gone.

Think of a library book. Fn is like reading a book that stays on the shelf. Anyone can read it, and it stays there. FnMut is like borrowing a book to write notes in the margins. You can read and write, but you must return it. FnOnce is like buying the book. You take it home, and it's no longer available to anyone else.

The trait name is a promise about what the closure does to its captures.

Minimal examples

The compiler infers the trait based on how you use the captured variables. You rarely write the trait explicitly. You write the closure body, and the compiler picks the most specific trait.

/// Demonstrates the three closure traits based on capture behavior.
fn main() {
    // Fn: Captures by immutable reference.
    // The closure reads `val` but does not change it.
    // It can be called multiple times.
    let val = 42;
    let read_val = || println!("Value: {}", val);
    read_val();
    read_val();

    // FnMut: Captures by mutable reference.
    // The closure increments `count`.
    // The closure variable must be declared mutable.
    let mut count = 0;
    let mut increment = || count += 1;
    increment();
    increment();
    println!("Count: {}", count);

    // FnOnce: Captures by move.
    // The closure takes ownership of `secret`.
    // It can only be called once.
    let secret = String::from("shhh");
    let reveal = || println!("Secret: {}", secret);
    reveal();
    // reveal(); // Error E0382: use of moved value `reveal`.
}

The compiler chooses the narrowest trait that satisfies the code. If you only read, you get Fn. If you mutate, you get FnMut. If you move, you get FnOnce.

The trait hierarchy

The traits form a hierarchy. Fn is a subtype of FnMut. FnMut is a subtype of FnOnce. This means a closure that implements Fn also implements FnMut and FnOnce. A closure that implements FnMut also implements FnOnce.

This hierarchy enables flexibility. A function that accepts FnOnce can take any closure. A function that accepts Fn can only take closures that don't mutate or move.

/// Shows that Fn satisfies FnMut, and FnMut satisfies FnOnce.
fn takes_fn_once<F: FnOnce()>(f: F) {
    // Accepts any closure.
    f();
}

fn takes_fn_mut<F: FnMut()>(f: F) {
    // Accepts FnMut and Fn.
    f();
}

fn takes_fn<F: Fn()>(f: F) {
    // Accepts only Fn.
    f();
}

fn main() {
    // This closure only reads, so it implements Fn.
    let f = || println!("Hello");

    takes_fn(f);      // Exact match.
    takes_fn_mut(f);  // Fn is a subtype of FnMut.
    takes_fn_once(f); // FnMut is a subtype of FnOnce.
}

The hierarchy works because a closure that only reads can safely be used where mutation is allowed. A closure that mutates can safely be used where a one-time move is allowed. The reverse is not true. A closure that moves data cannot be used where borrowing is required.

Accept the most general trait your function needs. This allows callers to pass the widest range of closures.

Realistic usage: threads and iterators

The choice of trait matters in real code. std::thread::spawn requires FnOnce. The closure is moved into the new thread. It runs exactly once. The closure can move data into the thread.

/// Thread spawn requires FnOnce because the closure moves into the new thread.
use std::thread;

fn main() {
    let data = vec![1, 2, 3];

    // The closure moves `data` into the thread.
    // This requires FnOnce.
    let handle = thread::spawn(move || {
        println!("Thread has data: {:?}", data);
    });

    handle.join().unwrap();
}

If thread::spawn required Fn, you could not move data into the thread. You would be forced to clone the data or use interior mutability. FnOnce gives you the freedom to move ownership.

Iterators use FnMut. Methods like map and filter call the closure multiple times. The closure might mutate state. FnMut allows this while ensuring the closure can be called repeatedly.

/// Iterator methods use FnMut to allow stateful closures.
fn main() {
    let mut sum = 0;

    // The closure mutates `sum`.
    // It implements FnMut.
    let result = (1..=5)
        .map(|x| {
            sum += x;
            x * 2
        })
        .collect::<Vec<_>>();

    println!("Result: {:?}", result);
    println!("Sum: {}", sum);
}

The iterator calls map five times. The closure mutates sum each time. FnMut makes this possible. If map required Fn, you could not accumulate state in the closure.

Use FnOnce for one-shot operations that consume data. Use FnMut for repeated operations that might mutate state. Use Fn for read-only callbacks.

Pitfalls and compiler errors

The most common error is calling a FnOnce closure twice. The closure moves its captures on the first call. The second call tries to use moved values.

let x = String::from("data");
let f = || println!("{}", x);
f();
f(); // Error E0382: use of moved value `f`.

The compiler rejects the second call with E0382 (use of moved value). The closure moved x on the first call. The closure itself is consumed. You cannot call it again.

Another error is forgetting mut on a FnMut closure. The closure variable must be mutable if the closure mutates captures.

let mut count = 0;
let f = || count += 1; // Error E0596: cannot borrow as mutable.
f();

The compiler rejects this with E0596 (cannot borrow as mutable). You need let mut f. The closure borrows count mutably. The closure variable must allow mutation.

A subtle trap involves the move keyword. move forces the closure to capture by value. If the captured value is not Copy, the closure becomes FnOnce.

let x = String::from("data");
let f = move || println!("{}", x);
f();
// f(); // Error E0382.

move captures x by value. String is not Copy. The closure takes ownership of x. The closure implements FnOnce. You cannot call it twice.

move does not mean "call once". It means "capture by value". But capturing a non-Copy value by value usually makes the closure FnOnce.

Trust the borrow checker. It is protecting you from double-freeing and data races. The error message tells you exactly what the closure captured and how.

API design conventions

When writing functions that accept closures, choose the trait bound carefully. The bound determines what closures callers can pass.

Accept FnOnce when you call the closure exactly once. This is the most flexible bound. Callers can pass closures that move data, mutate state, or only read.

Accept FnMut when you call the closure multiple times and the closure might mutate state. This is the standard for iterators. Callers can pass closures that mutate or only read.

Accept Fn when you call the closure multiple times and the closure does not mutate state. This is the standard for read-only callbacks. Callers can only pass closures that read.

The community convention is to accept the most general trait your implementation needs. If you only call the closure once, accept FnOnce. If you call it multiple times but never mutate, accept Fn. This maximizes flexibility for the caller.

/// Accepts FnOnce to allow callers to move data into the closure.
fn run_once<F: FnOnce()>(f: F) {
    f();
}

/// Accepts Fn to ensure the closure can be called repeatedly without mutation.
fn run_twice<F: Fn()>(f: F) {
    f();
    f();
}

fn main() {
    let data = String::from("hello");

    // This closure moves `data`. It implements FnOnce.
    // It works with run_once.
    run_once(|| println!("{}", data));

    // It does not work with run_twice.
    // run_twice(|| println!("{}", data)); // Error.
}

The run_once function accepts the closure that moves data. The run_twice function rejects it. The trait bound enforces the contract.

Convention aside: cargo fmt formats closure syntax consistently. Don't argue about spacing. Focus on the trait bound. The bound is the logic.

Decision matrix

Use Fn when the closure reads captured variables and you call it multiple times. This is the standard for read-only callbacks and pure functions.

Use FnMut when the closure mutates captured variables or you need to call it multiple times and the compiler detects mutation. This is the default for iterators and stateful callbacks.

Use FnOnce when the closure moves captured variables or you only call it once. This is the most flexible bound for function arguments and is required for thread spawning.

Use FnOnce in function signatures to accept the widest range of closures. This allows callers to move data into the closure.

Use Fn in function signatures when you need to call the closure repeatedly without mutation. This prevents callers from passing closures that consume data.

Use FnMut in function signatures when you iterate or the closure might mutate state. This balances flexibility and safety for repeated calls.

Reach for Fn when you want to guarantee the closure doesn't change state. Reach for FnMut when you need iteration with state. Reach for FnOnce when you need to consume data or run a one-shot task.

Where to go next