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:

  1. 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
    }
    }
  2. 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
}