Module 5 • Lesson 27

Closures and Capturing

📚 9 min read💻 Free Course🦀 nixus.pro

Closures: Anonymous Functions

fn main() {
    // Closure syntax: |params| body
    let add = |a, b| a + b;
    let double = |x: i32| x * 2;
    let greet = |name: &str| format!("Hello, {}!", name);

    println!("{}", add(2, 3));
    println!("{}", greet("Rust"));

    // Closures CAPTURE their environment
    let factor = 3;
    let multiply = |x| x * factor;  // Captures factor by copy
    println!("{}", multiply(5)); // 15

    let text = String::from("hello");
    let contains_l = |c| text.contains(c);  // Captures text by reference
    println!("{}", contains_l('l'));
    println!("{}", text); // text still valid

    // move closure: take ownership of captured values
    let text2 = String::from("world");
    let owns_text = move || format!("I own: {}", text2);  // text2 moved in
    println!("{}", owns_text());
    // println!("{}", text2); // ERROR: moved into closure

    // Closures as arguments (Fn, FnMut, FnOnce)
    fn apply i32>(f: F, x: i32) -> i32 { f(x) }
    fn apply_twice i32>(f: F, x: i32) -> i32 { f(f(x)) }

    println!("{}", apply(|x| x + 1, 5));        // 6
    println!("{}", apply_twice(|x| x * 2, 3));  // 12
}

Fn Traits

// Fn:     can be called multiple times, immutable capture
// FnMut:  can be called multiple times, mutable capture
// FnOnce: can only be called once (consumes captured vars)

fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
    move |x| x + n  // Returns a closure!
}

fn make_counter() -> impl FnMut() -> i32 {
    let mut count = 0;
    move || { count += 1; count }  // FnMut: modifies captured state
}

fn main() {
    let add5 = make_adder(5);
    println!("{} {}", add5(10), add5(20)); // 15 25

    let mut counter = make_counter();
    println!("{} {} {}", counter(), counter(), counter()); // 1 2 3

    // Sort with closure
    let mut names = vec!["Charlie", "Alice", "Bob"];
    names.sort_by(|a, b| a.len().cmp(&b.len()));
    println!("{:?}", names); // ["Bob", "Alice", "Charlie"]
}

🎯 Practice

  1. Write a function make_multiplier(n: i32) -> impl Fn(i32) -> i32 returning a closure
  2. Write fn compose<A,B,C>(f: impl Fn(A)->B, g: impl Fn(B)->C) -> impl Fn(A)->C
  3. Use sort_by_key with a closure to sort Vec<(String, i32)> by the i32 field descending

🎉 Key Takeaways