12.5 Advanced Topics
Finally, let’s briefly touch upon a few more advanced aspects of using closures.
12.5.1 Returning Closures
Since each closure has a unique, unnameable type, functions must return them opaquely:
-
impl Trait
: Preferred. Returns an opaque type implementing the trait(s). Enables static dispatch.#![allow(unused)] fn main() { fn make_adder(a: i32) -> impl Fn(i32) -> i32 { move |b| a + b // Returns a specific, unnamed closure type } }
-
Box<dyn Trait>
: Returns a trait object on the heap. Requires heap allocation and dynamic dispatch, but allows returning different closure types.#![allow(unused)] fn main() { fn make_adder_boxed(a: i32) -> Box<dyn Fn(i32) -> i32> { Box::new(move |b| a + b) } }
12.5.2 Precise Field Capturing
With move
closures, Rust often captures only the specific fields of a struct that are actually used within the closure, rather than moving the entire struct.
struct Settings { mode: String, retries: u32 } fn main() { let mut settings = Settings { mode: "fast".to_string(), retries: 3 }; // 'move' closure only uses 'settings.retries'. let get_retries = move || settings.retries; // Only 'retries' was moved; 'mode' remains accessible via 'settings'. settings.mode = "slow".to_string(); println!("Mode: {}", settings.mode); // Output: Mode: slow let retries_val = get_retries(); println!("Retries: {}", retries_val); // Output: Retries: 3 // Cannot access settings.retries here as it was moved. // println!("Retries after move: {}", settings.retries); // Error }