Who the hell are Fn, FnMut, and FnOnce? Rust closures under the microscope

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Closures in Rust: magic or strict mathematics?

\n\n

If you've ever written something like |x| x + 1 in Rust, you've used closures. But as soon as you try to pass this closure to a function or store it in a struct, the compiler suddenly starts demanding: "Which trait? Fn, FnMut, or FnOnce?".

\n\n

And many people get stuck here. Especially if you come from JavaScript, Python, or Ruby, where closures are "just functions that capture variables." In Rust, it's more complicated (and more interesting).

\n\n

Today we'll figure out what actually happens under the hood when Rust decides which trait to give your closure. This isn't magic — it's a strict type system that protects you from data races and dangling pointers. Let's go!

\n\n

The gist: three closure traits

\n\n

In Rust, there are three traits that closures implement:

\n\n
    \n
  • FnOnce — can be called once. Takes ownership of captured variables.
  • \n
  • FnMut — can be called multiple times, but can mutate captured variables (takes &mut).
  • \n
  • Fn — can be called multiple times and does not mutate captured variables (takes &).
  • \n
\n\n

Each closure automatically implements all applicable traits. That is, if a closure implements Fn, it automatically implements both FnMut and FnOnce. But the reverse does not work.

\n\n

Why? Because Fn is the most restrictive, and FnOnce is the least restrictive. The inheritance hierarchy looks like this:

\n\n
FnOnce  ←  FnMut  ←  Fn
\n\n

That is, FnMut: FnOnce, and Fn: FnMut. Remember this ladder — it's key.

\n\n

How does Rust decide which trait to give a closure?

\n\n

The compiler looks at how exactly the closure uses captured variables. There are three capture modes:

\n\n
    \n
  • By reference (&T) — the closure only reads the variable.
  • \n
  • By mutable reference (&mut T) — the closure mutates the variable.
  • \n
  • By value (T) — the closure takes ownership of the variable.
  • \n
\n\n

Here's how this affects the traits:

\n\n\n \n \n \n \n
Capture modeImplemented traitsExample
Only &TFn + FnMut + FnOnce|| println!("{}", x)
&mut T (but not T)FnMut + FnOnce|| x += 1
T (ownership)Only FnOnce|| drop(x) or move || x
\n\n

Let's break down each case with live code.

\n\n

Example 1: Read-only → Fn

\n\n
let x = 42;\nlet read_closure = || println!("x = {}", x);\n\n// Can be called any number of times\nread_closure();\nread_closure();\n\n// And even passed to a function requiring Fn\nfn call_fn<F: Fn()>(f: F) {\n    f();\n}\ncall_fn(read_closure);
\n\n

Here the closure simply reads x. It doesn't need to mutate it or take ownership. Rust understands: "OK, this is safe — we give Fn".

\n\n

Example 2: Mutation → FnMut

\n\n
let mut x = 42;\nlet mut mut_closure = || {\n    x += 1;\n    println!("x = {}", x);\n};\n\nmut_closure(); // x = 43\nmut_closure(); // x = 44\n\n// Pass to a function requiring FnMut\nfn call_fn_mut<F: FnMut()>(mut f: F) {\n    f();\n    f();\n}\ncall_fn_mut(mut_closure);
\n\n

Notice: we declared mut_closure as let mut. Why? Because calling FnMut requires a muta

Blogs

Book Recommendations