DEV Community

이관호(Gwanho LEE)
이관호(Gwanho LEE)

Posted on

Closures in Rust: `Fn`, `FnMut`, and `FnOnce`

Closures in Rust are one of its most expressive and powerful features. They allow you to create lightweight functions that can capture variables from their surrounding environment. Rust closures are especially valuable in asynchronous programming, iterator chains, and function composition. In this post, we’ll explore the three closure traits—Fn, FnMut, and FnOnce—and look at how they are used in real Rust standard library code.


🔍 What Are Closures?

Closures are anonymous functions that can capture variables from their environment. Depending on how a closure captures these variables, Rust classifies them into one of three traits:

🔹 Fn

  • Captures variables by reference
  • Can be called multiple times
  • Used when the closure only needs read access

🔹 FnMut

  • Captures variables by mutable reference
  • Can mutate captured variables
  • Can be called multiple times

🔹 FnOnce

  • Captures variables by value (ownership)
  • Can be called only once
  • Used when the closure consumes the captured variable

Rust automatically infers the appropriate trait for your closure depending on how it is used.


🧠 Real-World Examples from the Standard Library

FnOnce in Option::map_or

#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use = "if you don't need the returned value, use `if let` instead"]
pub fn map_or<U, F>(self, default: U, f: F) -> U
where
    F: FnOnce(T) -> U,
{
    match self {
        Some(t) => f(t),
        None => default,
    }
}
Enter fullscreen mode Exit fullscreen mode

This function takes an Option<T> and either returns the result of a closure f if the Option is Some, or a default value if it is None. Because the closure may consume its input, it uses FnOnce.

FnMut in Iterators

pub fn for_each<F>(self, mut f: F)
where
    Self: Sized,
    F: FnMut(Self::Item),
{
    for item in self {
        f(item);
    }
}
Enter fullscreen mode Exit fullscreen mode

This method calls the closure f on each element of an iterator. Since the closure is called multiple times and may mutate its state, it uses FnMut.

Fn in filter

pub fn filter<P>(self, predicate: P) -> Filter<Self, P>
where
    Self: Sized,
    P: Fn(&Self::Item) -> bool,
{
    Filter::new(self, predicate)
}
Enter fullscreen mode Exit fullscreen mode

The filter method in iterators uses a closure to test each element. Since this closure only reads data and doesn't mutate or consume captured values, Fn is appropriate.


💡 Why This Matters

Closures are essential for writing expressive and safe code in Rust. Understanding these traits helps you:

  • Write efficient iterator chains
  • Build safe asynchronous systems
  • Compose functions with minimal overhead
  • Understand ownership and lifetimes more deeply

✅ Benefits of Closures in Rust

  • Zero-cost abstraction: No performance hit at runtime
  • Memory safety: Compile-time checking of access and mutation
  • Functional programming patterns: Map, filter, fold, etc.
  • Strong type inference: No need to manually annotate closure types in most cases

🧠 Summary

Closures in Rust are not only syntactic sugar—they're backed by powerful traits: Fn, FnMut, and FnOnce. These traits describe how a closure interacts with its captured environment and determine how and where the closure can be used. Understanding them is critical for any Rustacean aiming for mastery.

Top comments (0)