12.7 Additional Topics
Below are a few advanced patterns and features related to closures.
12.7.1 Returning Closures
You can return closures from functions in two ways:
Using a Trait Object
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
Trait objects allow returning different closure types but require dynamic dispatch and potentially a heap allocation.
Using impl Trait
fn returns_closure() -> impl Fn(i32) -> i32 {
|x| x + 1
}
Here, the compiler monomorphizes the code, often optimizing as if it were a normal function.
12.7.2 Partial Captures
Modern Rust partially captures only the fields of a struct that the closure uses, reducing unnecessary moves. This helps when you only need to capture part of a larger data structure:
struct Container { data: Vec<i32>, label: String, } fn main() { let c = Container { data: vec![1, 2, 3], label: "Numbers".to_string(), }; // Only moves c.data into the closure let consume_data = move || { println!("Consumed data: {:?}", c.data); }; // c.label is still accessible println!("Label is still available: {}", c.label); consume_data(); }
12.7.3 Real-World Use Cases
- GUIs: Closures as event handlers, triggered by user actions.
- Async / Futures: Passing closures to asynchronous tasks.
- Configuration / Strategy: Using closures for custom logic in libraries or frameworks.