In Rust, storing closures typically involves generics and trait objects. Because closures can capture the environment, they are treated as anonymous functions with varying types. All closures in Rust implement one of the traits Fn, FnMut, or FnOnce.
Using Generics to Store Closures
Using generics enables a struct to store any closure type that implements the specified trait. This approach avoids the overhead of dynamic allocation and indirect calls, but it requires the closure type to be known at compile-time and the struct to be generic.
ruststruct MyStruct<T> where T: Fn(i32) -> i32, { closure: T, } impl<T> MyStruct<T> where T: Fn(i32) -> i32, { fn new(closure: T) -> Self { MyStruct { closure } } fn call(&self, arg: i32) -> i32 { (self.closure)(arg) } } fn main() { let add_one = |x: i32| x + 1; let my_struct = MyStruct::new(add_one); println!("Result: {}", my_struct.call(5)); // Output: Result: 6 }
Using Box<dyn Fn()> to Store Closures
If you need to store a closure that can change at runtime within a struct, or if you prefer not to make the struct generic due to the closure, you can use trait objects. This typically involves boxing the closure, which enables dynamic allocation on the heap. Using Box<dyn Fn()> allows you to store and call different closures at runtime.
ruststruct MyStruct { closure: Box<dyn Fn(i32) -> i32>, } impl MyStruct { fn new<F>(closure: F) -> Self where F: 'static + Fn(i32) -> i32, { MyStruct { closure: Box::new(closure) } } fn call(&self, arg: i32) -> i32 { (self.closure)(arg) } } fn main() { let multiply_by_two = |x: i32| x * 2; let my_struct = MyStruct::new(multiply_by_two); println!("Result: {}", my_struct.call(5)); // Output: Result: 10 }
Choosing the Right Method
- Generic approach: Suitable when you know the closure type beforehand and wish to avoid runtime performance overhead.
- Trait object approach: Suitable when you need to change closures at runtime or prefer not to make the struct generic because of the closure type.
Each method has its own use case, and you can select the most appropriate approach based on your specific needs.