12.3 Common Use Cases for Closures

Closures excel at encapsulating behavior concisely.

12.3.1 Iterators

Used heavily with adapters like map, filter, fold:

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6];

    let evens: Vec<_> = numbers.iter()
        .filter(|&&x| x % 2 == 0) // Closure predicate
        .collect();
    println!("Evens: {:?}", evens); // Output: Evens: [2, 4, 6]

    let squares: Vec<_> = numbers.iter()
        .map(|&x| x * x) // Closure transformation: takes &i32, dereferences to i32
        .collect();
    println!("Squares: {:?}", squares); // Output: Squares: [1, 4, 9, 16, 25, 36]
}

12.3.2 Custom Sorting

sort_by and sort_by_key use closures for custom logic:

#[derive(Debug)] struct Person { name: String, age: u32 }

fn main() {
    let mut people = vec![
        Person { name: "Alice".to_string(), age: 30 },
        Person { name: "Bob".to_string(), age: 25 },
        Person { name: "Charlie".to_string(), age: 35 },
    ];

    // Sort by age using 'sort_by_key'
    people.sort_by_key(|p| p.age); // Closure extracts the key
    println!("Sorted by age: {:?}", people);

    // Sort by name length using 'sort_by'
    people.sort_by(|a, b| a.name.len().cmp(&b.name.len())); // Closure compares elements
    println!("Sorted by name length: {:?}", people);
}

12.3.3 Lazy Initialization

Option::unwrap_or_else, Result::unwrap_or_else compute defaults lazily:

fn main() {
    let config_path: Option<String> = None;

    let path = config_path.unwrap_or_else(|| {
        println!("Computing default path..."); // Runs only if None
        String::from("/etc/default.conf")
    });

    println!("Using path: {}", path);
    // Output: Computing default path...
    // Output: Using path: /etc/default.conf
}

12.3.4 Concurrency and Asynchronous Operations

Essential for passing code (often with captured state via move) to threads or async tasks.