In this lesson, you will learn how to make your own custom types behave like functions by implementing the Fn, FnMut, or FnOnce traits.
Why Make a Type Callable?
In Rust, functions and closures are types that implement specific traits. By manually implementing these traits for your own structs, you can create objects that hold internal state while still being "invokable" using the familiar () syntax. This is powerful for building high-order APIs, such as custom filters, stateful callbacks, or command patterns.
The Trio of Traits
The Rust compiler provides three traits for callables, representing different levels of access to the data inside your struct:
FnOnce: The base trait. It consumes the object. You use this if your callable can only be run once because it moves its internal data.FnMut: Inherits fromFnOnce. It allows the callable to modify its internal state by taking&mut self.Fn: Inherits fromFnMutandFnOnce. It allows the callable to be called multiple times without modifying the state, taking&self.
Concrete Example: A Stateful Counter
Imagine you want a callable object that tracks how many times it has been executed:
struct Counter {
count: u32,
}
impl FnMut<()> for Counter {
extern "rust-call" fn call_mut(&mut self, _: ()) -> Self::Output {
self.count += 1;
println!("Called {} times", self.count);
}
}
Note: Implementing these traits requires the fn_traits feature flag in nightly Rust, as they are considered unstable for manual implementation. However, understanding the hierarchy is essential for designing APIs that accept generic closures.
When you design a function to accept a callback, you define a generic bound like F: Fn(i32) -> i32. Because you know the relationship between Fn, FnMut, and FnOnce, you can write flexible APIs that accept any closure regardless of whether it needs to mutate state or consume its environment.
Key Takeaway: By implementing Fn, FnMut, or FnOnce, you allow your custom types to act as first-class functions, enabling stateful behavior within high-order APIs.