← Back to Rust Course | Chapter 14: Closures, Iterators & Async | Lesson 1 of 6

Closure Traits: Fn, FnMut, FnOnce

There are three flavors of closures depending on whether they just look at, change, or use up the things they grabbed.
Syntax
rust
fn name<F: Fn(Param) -> Ret>(f: F) {}      // borrows
fn name<F: FnMut(Param) -> Ret>(mut f: F) {} // mutates
fn name<F: FnOnce() -> Ret>(f: F) {}        // consumes

Fn: Read-Only Captures

A closure that only reads captured variables implements Fn, and can be called multiple times without any restriction.

Example: Fn: Read-Only Captures

markup
// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `greeting`, set to `String::from("Hello")`
    let greeting = String::from("Hello");
    // Declare an immutable variable `greet`, set to `|| println!("{}, world!", greeting)`
    let greet = || println!("{}, world!", greeting);
    greet();
    greet();
}

FnMut: Mutating Captures

A closure that mutates a captured variable implements FnMut; the variable holding the closure must itself be mut to call it.

Example: FnMut: Mutating Captures

markup
// Entry point: execution of the program starts here
fn main() {
    // Declare a mutable variable `count`, initialized to `0`
    let mut count = 0;
    // Declare a mutable variable `increment`, initialized to `|| {`
    let mut increment = || {
        count += 1;
        // Print "count is now {}" to the console, substituting in `count` (with a trailing newline)
        println!("count is now {}", count);
    };
    increment();
    increment();
}

FnOnce: Consuming Captures

A closure that moves a captured variable out of itself (for example, returning owned data) implements only FnOnce, meaning it can be called at most one time.

Example: FnOnce: Consuming Captures

markup
// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `text`, set to `String::from("consumed")`
    let text = String::from("consumed");
    // Declare an immutable variable `consume`, set to `move || {`
    let consume = move || {
        // Declare an immutable variable `owned`, set to `text`
        let owned = text;
        // Print "Consumed inside closure: {}" to the console, substituting in `owned` (with a trailing newline)
        println!("Consumed inside closure: {}", owned);
    };
    consume();
}

Forcing Ownership with move

The move keyword forces a closure to take ownership of every variable it captures, rather than borrowing them, which is required when the closure needs to outlive its original scope.

Example: Forcing Ownership with move

markup
// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `data`, set to `vec![1, 2, 3]`
    let data = vec![1, 2, 3];
    // Declare an immutable variable `owns_data`, set to `move || println!("Owned inside closure: {:?}", data)`
    let owns_data = move || println!("Owned inside closure: {:?}", data);
    owns_data();
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Trying to call an FnOnce closure more than once -- by definition it can only be called a single time.
  2. Requiring Fn in a function signature when the closure passed actually needs to mutate captured state, requiring FnMut instead.
  3. Forgetting the move keyword forces a closure to take ownership of captured variables instead of borrowing them.
Chapter Summary
  • Fn closures can be called repeatedly and only borrow captured variables immutably.
  • FnMut closures can be called repeatedly and may mutate captured variables.
  • FnOnce closures can only be called once, since they consume (move) captured variables.
  • The move keyword forces a closure to take ownership of the variables it captures rather than borrowing them.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.