12.4 Working with Closures

12.4.1 Using Closures with Iterator Methods

Closures are often used with iterator methods like map, filter, and for_each.

Example: Using filter with a Closure

#![allow(unused)]
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let even_numbers: Vec<_> = numbers.into_iter().filter(|x| x % 2 == 0).collect();
println!("{:?}", even_numbers); // Output: [2, 4, 6]
}
  • The closure |x| x % 2 == 0 filters out even numbers.
  • Note: Iterators are discussed in detail in the next chapter.

12.4.2 Sorting Collections with Closures

Closures can be used to define custom sorting behavior using the sort_by_key method.

Example: Sorting Structs by a Field

#[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 },
    ];
    people.sort_by_key(|person| person.age);
    println!("{:?}", people);
}
  • The closure |person| person.age extracts the age field for sorting.
  • sort_by_key is cleaner and easier to understand than sort_by.
  • The closure borrows person immutably.

12.4.3 Using Closures with unwrap_or_else

Closures are used in methods like unwrap_or_else to provide lazy evaluation of default values.

Example:

#![allow(unused)]
fn main() {
let config: Option<String> = None;
let config_value = config.unwrap_or_else(|| {
    println!("Using default configuration");
    "default_config".to_string()
});
println!("Config: {}", config_value);
}
  • The closure is called only if config is None.
  • Allows for computation of the default value only when necessary.