12.6 Performance Considerations
12.6.1 Do Closures Require Heap Allocation?
Closures in Rust are represented as structs generated by the compiler. Whether they require heap allocation depends on how they are used:
-
Stack Allocation: When a closure's size is known at compile time and it doesn't need to be stored beyond the current scope, it can be stack-allocated.
Example Without Heap Allocation:
#![allow(unused)] fn main() { let add_one = |x| x + 1; let result = add_one(5); }
- The closure is stored on the stack.
-
Heap Allocation: When you need to store a closure in a trait object (
Box<dyn Fn()>
), it may involve heap allocation.Example With Heap Allocation:
#![allow(unused)] fn main() { let closure_factory = || { let x = 10; move |y| x + y }; let boxed_closure: Box<dyn Fn(i32) -> i32> = Box::new(closure_factory()); }
- The closure is stored in a
Box
, which allocates on the heap.
- The closure is stored in a
12.6.2 Performance of Closures vs. Functions
Closures can be as efficient as regular functions:
- Inlining: The compiler can inline closures, eliminating function call overhead.
- Optimizations: Rust's optimizer can remove unnecessary allocations.
- Trait Objects: Using trait objects for closures (
Box<dyn Fn()>
) can introduce dynamic dispatch overhead.
Best Practices:
- Avoid Unnecessary Heap Allocation: Use concrete types or generics instead of trait objects when possible.
- Minimize Dynamic Dispatch: Prefer static dispatch by using generic parameters (
impl Fn()
) instead of trait objects.