Course contentsShow
← Rust
Lesson 570 of 1,9105. Type System and TraitsFree lesson

Closure Capture Rules and Traits

Control the ownership behavior of closures using Fn trait hierarchies.

In this lesson, you will learn how Rust’s closure traits (Fn, FnMut, and FnOnce) dictate exactly how a closure is allowed to capture and use variables from its surrounding environment.

The Hierarchy of Capture

In Rust, a closure isn't just a function; it is a struct that captures its environment. Depending on what the closure does with those captured variables, the compiler automatically assigns it one of three "traits" that define its behavior:

  1. FnOnce: This closure takes ownership of the variables it captures. Because it consumes them, it can only be called once.
  2. FnMut: This closure captures variables by mutable reference (&mut). It can modify the values it captures, so it can be called multiple times as long as it has mutable access.
  3. Fn: This closure captures variables by immutable reference (&). Since it doesn't change anything, it can be called repeatedly, even in parallel.

Seeing the Rules in Action

Consider this example where we move a variable into a closure:

let name = String::from("Rustacean");

// This closure takes ownership of 'name'.
// Because it consumes 'name', it implements 'FnOnce'.
let consume_name = || println!("Hello, {}", name);

consume_name();
// consume_name(); // Error: 'name' moved into the closure on the first call.

If you only need to read the data, Rust prefers the least restrictive trait (Fn). However, if you add code that modifies a captured variable, the compiler "upgrades" the requirement to FnMut. If you move the variable, it defaults to FnOnce.

Why the Traits Matter

These traits act as gatekeepers. When you write a function that accepts a closure as an argument, you use these traits as bounds. If you write fn run_it<F: Fn()>(f: F), you are promising the caller that this closure will not mutate or consume its environment. This allows Rust to guarantee safety even when passing closures across threads or into complex data structures.

Key Takeaway: Rust automatically assigns the most restrictive trait (FnOnce, FnMut, or Fn) based on how your closure interacts with captured variables, ensuring that you can only call a closure in ways that are memory-safe.