Question
How can I store a closure in a Rust struct using modern Rust syntax?
Before Rust 1.0, it was possible to write a struct with an obsolete closure type:
struct Foo {
pub foo: |usize| -> usize,
}
A generic version can be written as follows:
struct Foo<F: FnMut(usize) -> usize> {
pub foo: F,
}
However, what is the type of a Foo value created from a closure?
let foo: Foo<???> = Foo { foo: |x| x + 1 };
Alternatively, a closure could be stored through a mutable reference:
struct Foo<'a> {
pub foo: &'a mut dyn FnMut(usize) -> usize,
}
Is this reference-based approach slower because it requires pointer dereferencing and dynamic dispatch instead of specializing for the concrete closure type?
Short Answer
Rust closures have anonymous, compiler-generated types. Usually, you store one in a generic struct and let type inference determine that type. Use a trait object such as Box<dyn FnMut(...)> or &mut dyn FnMut(...) only when the concrete closure type must be hidden or chosen at runtime. This page explains the ownership, lifetime, and performance trade-offs.
Concept
A closure is a callable value. It may also capture values from the surrounding scope:
let amount = 10;
let add_amount = |value| value + amount;
Rust gives every closure expression its own unique, anonymous type. That type contains any captured data and implements one or more of the callable traits:
Fn: can be called through an immutable reference; it does not mutate or consume captured values.FnMut: may mutate captured values.FnOnce: may consume captured values, so it can be called at most once.
Because a closure's concrete type has no name you can write directly, this does not work as an explicit annotation:
// There is no type name that can replace ??? here.
let foo: Foo<???> = Foo { foo: |x| x + 1 };
Instead, make Foo generic and let Rust infer its F type:
let foo = Foo { foo: |x| x + 1 };
This is important because the generic version stores the closure directly, preserving its exact type. Rust can use static dispatch and can often inline the closure call, which is typically as efficient as writing the operation directly.
Mental Model
Think of a closure as a custom tool made for one particular job.
- A generic
Foo<F>is a box made to fit one exact tool shape. When you build the box, Rust measures the tool automatically. The tool is stored directly inside, with no extra lookup needed. - A trait object (
dyn FnMut) is a standard adapter socket. It can hold many kinds of compatible tools, but using it requires following the adapter to find the correct calling behavior at runtime. - A reference (
&mut dyn FnMut) meansFooborrows someone else's tool rather than owning it. - A
Box<dyn FnMut>meansFooowns a tool stored elsewhere on the heap.
Use the custom-fit generic box when one concrete closure type is known at compile time. Use the adapter when code needs to handle different closure types through one common type.
Syntax and Examples
A generic struct can store any closure type that implements FnMut(usize) -> usize:
struct Foo<F>
where
F: FnMut(usize) -> usize,
{
foo: F,
}
impl<F> Foo<F>
where
F: FnMut(usize) -> usize,
{
fn apply(&mut self, value: usize) -> usize {
(self.foo)(value)
}
}
fn main() {
let mut foo = Foo { foo: |x| x + 1 };
assert_eq!(foo.apply(4), 5);
}
There is no explicit type annotation on foo. The compiler infers a hidden closure type for F from |x| x + 1.
apply takes &mut self because calling an FnMut closure requires mutable access to the closure. A closure that does not mutate captured state can implement Fn, but it also satisfies an FnMut requirement.
If the closure only needs Fn, use the narrower bound:
struct Transformer<F: Fn(usize) -> usize> {
transform: F,
}
A narrower trait communicates the least amount of capability required by the API.
Step by Step Execution
Consider a closure that changes captured state:
struct Counter<F: FnMut(usize) -> usize> {
operation: F,
}
impl<F: FnMut(usize) -> usize> Counter<F> {
fn run(&mut self, input: usize) -> usize {
(self.operation)(input)
}
}
fn main() {
let mut calls = 0;
let mut counter = Counter {
operation: |value| {
calls += 1;
value + calls
},
};
assert_eq!(counter.run(10), 11);
assert_eq!(counter.run(10), 12);
}
Execution steps:
callsstarts with the value0.- The closure captures
callsby mutable reference because it changescalls. Counterstores the closure directly as its genericFfield.- The first
run(10)call invokes the closure. It incrementscallsto1and returns10 + 1. - The second call increments
callsto2and returns10 + 2. - Since calling the closure changes captured state, the closure implements
FnMut, and bothcounterandrunmust be mutable.
The borrow of calls lasts as long as counter exists, because the stored closure still uses it.
Real World Use Cases
Closures stored in structs are useful when an object needs customizable behavior.
- Sorting and ranking: Store a comparison or scoring function.
- Validation: Store a rule that decides whether input is acceptable.
- Retry policies: Store logic that decides whether an operation should be retried.
- Data transformations: Store a function that normalizes values before saving or displaying them.
- Callbacks: Store behavior to run after a task completes.
- Testing: Inject a closure that simulates a clock, random-number source, network operation, or external service.
For example, a validator can accept a closure chosen by the caller:
struct Validator<F: Fn(&str) -> bool> {
is_valid: F,
}
impl<F: Fn(&str) -> bool> Validator<F> {
fn validate(&self, input: &str) -> bool {
(self.is_valid)(input)
}
}
fn main() {
let username_validator = Validator {
is_valid: |name| name.len() >= 3 && !name.contains(' '),
};
assert!(username_validator.validate("rustacean"));
assert!(!username_validator.validate("no"));
}
Real Codebase Usage
In production Rust code, generic closure fields are common when the closure type naturally belongs to the surrounding generic type.
Generic callback with static dispatch
struct Processor<F> {
on_success: F,
}
impl<F> Processor<F>
where
F: FnMut(String),
{
fn process(&mut self, value: String) {
// Process the value...
(self.on_success)(value);
}
}
This is a good fit when a Processor is created with one callback and its concrete type does not need to be hidden.
Trait object for runtime-selected callbacks
A trait object is useful when different branches must produce the same return type:
struct Processor {
on_success: Box<dyn FnMut(String)>,
}
fn make_processor(logging_enabled: bool) -> Processor {
if logging_enabled {
Processor {
on_success: Box::new(|value| println!("Processed: {value}")),
}
} else {
Processor {
on_success: Box::new(|_| {}),
}
}
}
The two closures have different anonymous types. Box<dyn FnMut(String)> erases those differences, allowing both branches to return Processor.
Borrowed callback
Use &mut dyn FnMut when the callback is owned elsewhere and only needed temporarily:
fn apply_to_values(values: &[usize], callback: &mut dyn FnMut(usize)) {
for &value in values {
callback(value);
}
}
This avoids heap allocation, but the caller must keep the closure alive for the duration of the borrow.
Common Mistakes
Trying to name a closure type
Every closure expression has its own anonymous type, so this cannot be completed:
// Invalid idea: closure types cannot be named in ordinary source code.
let value: Foo<???> = Foo { foo: |x| x + 1 };
Let inference work instead:
let value = Foo { foo: |x| x + 1 };
Forgetting mut for FnMut
Calling an FnMut closure requires mutable access:
let foo = Foo { foo: |x| x + 1 };
// let result = (foo.foo)(3); // Error: `foo` is not mutable.
Fix it with mut:
let mut foo = Foo { foo: |x| x + 1 };
let result = (foo.foo)(3);
Omitting dyn for a trait object
Modern Rust requires dyn when writing a trait object:
// Incorrect in modern Rust:
// foo: &'a mut FnMut(usize) -> usize,
Write:
foo: &'a mut dyn FnMut(usize) -> usize,
Borrowing a temporary closure
A struct containing a reference cannot outlive the closure it borrows. Do not return a reference to a local closure:
// This cannot work because the closure is dropped when the function returns.
// fn make_foo<'a>() -> Foo<'a> {
// let mut callback = |x| x + 1;
// Foo { foo: &mut callback }
// }
Return an owning generic struct or use Box<dyn FnMut(...)> when appropriate.
Choosing FnMut when only Fn is needed
FnMut works for many closures, but it requires mutable access to call. If your callback never changes captured state, prefer Fn for a simpler and more flexible API.
Expecting a trait object to hold a closure directly
A dyn FnMut(...) value has an unknown size, so it cannot be stored directly as a normal field. Put it behind a pointer such as &mut or Box:
struct OwnedCallback {
callback: Box<dyn FnMut(usize) -> usize>,
}
Comparisons
| Approach | Example field type | Ownership | Dispatch | Best use case |
|---|---|---|---|---|
| Generic closure | F where F: FnMut(usize) -> usize | The struct owns the closure | Static dispatch | Concrete closure type is known at compile time |
| Borrowed trait object | &'a mut dyn FnMut(usize) -> usize | Caller owns the closure | Dynamic dispatch | Temporary use of a callback owned elsewhere |
| Boxed trait object | Box<dyn FnMut(usize) -> usize> | The struct owns the boxed closure | Dynamic dispatch | Runtime-selected or heterogeneous closure types |
| Function pointer | fn(usize) -> usize | The struct stores a function address | Indirect function call | Plain functions or non-capturing closures only |
Generic closure vs trait object
A generic field gives each Foo<F> instantiation a distinct concrete type. This enables monomorphization: Rust generates code specialized for that F. It usually has no heap allocation and can be inlined.
A trait object uses one erased type for many possible closure types. Calling it generally involves an indirect call through a vtable. This small runtime cost is often irrelevant outside hot loops, but it can prevent inlining of the callback implementation.
&mut dyn FnMut vs Box<dyn FnMut>
&mut dyn FnMutborrows a closure. It has no allocation, but its lifetime is limited by the owner.Box<dyn FnMut>owns a heap-allocated closure. It is easier to return from functions and store long-term.
Fn, FnMut, and FnOnce
| Trait | Captured data behavior | Can call repeatedly? | Typical method receiver |
|---|---|---|---|
Fn | Reads captured data | Yes | &self |
FnMut | May mutate captured data | Yes | &mut self |
FnOnce | May move captured data out | Not necessarily | self |
Choose the least powerful bound your API needs.
Cheat Sheet
// Generic: compiler infers F from the closure.
struct Foo<F: FnMut(usize) -> usize> {
foo: F,
}
let mut foo = Foo { foo: |x| x + 1 };
let result = (foo.foo)(5);
// Borrowed trait object: caller owns the closure.
struct BorrowedFoo<'a> {
foo: &'a mut dyn FnMut(usize) -> usize,
}
// Owned trait object: heap allocation, one concrete struct type.
struct BoxedFoo {
foo: Box<dyn FnMut(usize) -> usize>,
}
// Function pointer: only plain functions and non-capturing closures.
struct FunctionFoo {
foo: fn(usize) -> usize,
}
Key rules:
- Closure types are anonymous; normally, let type inference infer them.
- Use a generic
Ffor static dispatch and direct storage. - Use
dyn Fn...only behind a pointer such as&,&mut, orBox. - Use
dynwhen spelling a trait object. - Calling
FnMutrequires a mutable closure or mutable struct value. - Prefer
FnoverFnMutwhen mutation is unnecessary. - A closure that captures local references cannot outlive those references.
FAQ
What is the type of a Rust closure?
Each closure expression has a unique anonymous type generated by the compiler. You generally cannot write that type yourself, so use type inference, a generic parameter, or a trait object.
Can I write Foo<???> for a closure?
No. There is no normal Rust syntax for naming the concrete type of |x| x + 1. Write let foo = Foo { foo: |x| x + 1 }; and let the compiler infer Foo<F>.
Is Box<dyn FnMut()> slower than a generic closure field?
It can be. A boxed trait object adds heap allocation when created and typically uses dynamic dispatch when called. A generic closure field uses static dispatch and may be inlined. Choose the trait object when its flexibility is needed, not by default.
Does &mut dyn FnMut allocate memory?
No. It borrows an existing closure and does not heap-allocate it. It still uses dynamic dispatch when called.
Why does FnMut require mut?
An FnMut closure is allowed to mutate its captured state. Rust therefore requires mutable access before it can be called, even if one particular closure does not currently mutate anything.
When should I use FnOnce?
Use FnOnce when your callback may consume captured values, such as moving a String out of the closure. Since that can make subsequent calls impossible, it is commonly used for one-time callbacks.
Can a non-capturing closure be stored as fn(usize) -> usize?
Yes. A closure that captures nothing can coerce to a function pointer when a fn type is expected. Capturing closures cannot, because they need storage for their captured data.
Mini Project
Description
Build a small number-processing pipeline that stores a configurable transformation closure. The generic version demonstrates direct closure storage and stateful FnMut behavior without needing to name the closure type.
Goal
Create a pipeline that transforms a list of numbers and records how many values it processed.
Requirements
Requirement 1
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.