๐Ÿฆ€ Functional Rust
๐ŸŽฌ Closures in Rust Fn/FnMut/FnOnce, capturing environment, move closures, higher-order functions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Closures capture variables from their environment โ€” by reference, mutable reference, or by value (move)

โ€ข Three traits: Fn (shared borrow), FnMut (mutable borrow), FnOnce (takes ownership)

โ€ข Higher-order functions like .map(), .filter(), .fold() accept closures as arguments

โ€ข move closures take ownership of captured variables โ€” essential for threading

โ€ข Closures enable functional patterns: partial application, composition, and strategy

513: Strategy Pattern via Closures

Difficulty: 3 Level: Intermediate Replace interchangeable algorithms with closures stored in structs โ€” no interfaces, no inheritance, no boilerplate.

The Problem This Solves

The classic Gang-of-Four Strategy pattern requires: a `Strategy` interface, one concrete class per algorithm, dependency injection plumbing, and often a factory. In Java this is 5 files for what amounts to "sometimes do A, sometimes do B." Without closures, Rust still requires traits and multiple implementing types. That's appropriate for complex strategies, but overkill when the "strategy" is just a comparison function, a discount calculation, or a formatting rule. When behavior varies but the shape of the algorithm is fixed (same inputs, same output type), closures let you express strategy as configuration data rather than code structure.

The Intuition

A strategy closure is a behavior stored as a value. Instead of calling `discount_strategy.apply(price)` on an object, you call `(self.discount)(price)` on a struct field. The closure is the strategy โ€” no interface needed. In Python, you'd pass a function: `sorter = Sorter(key=lambda x: x.name)`. In JavaScript, `array.sort((a, b) => a.price - b.price)` passes the comparison strategy inline. Rust's closures work the same way, with the addition that the closure type is checked at compile time. Use `F: Fn(...)` generics when the strategy is fixed at construction time. Use `Box<dyn Fn(...)>` when you need to store different strategies in the same struct or swap them at runtime.

How It Works in Rust

// Struct with a boxed closure strategy field
struct Sorter<T> {
 compare: Box<dyn Fn(&T, &T) -> std::cmp::Ordering>,
}

impl<T: Clone> Sorter<T> {
 fn new(compare: impl Fn(&T, &T) -> std::cmp::Ordering + 'static) -> Self {
     Sorter { compare: Box::new(compare) }
 }
 fn sort(&self, mut data: Vec<T>) -> Vec<T> {
     data.sort_by(|a, b| (self.compare)(a, b));
     data
 }
}

let nums = vec![3, 1, 4, 1, 5, 9];
// Three sorters โ€” same struct, different strategies
let asc  = Sorter::new(|a: &i32, b| a.cmp(b));
let desc = Sorter::new(|a: &i32, b| b.cmp(a));
let abs_desc = Sorter::new(|a: &i32, b| b.abs().cmp(&a.abs()));

// Runtime strategy selection
let use_premium = true;
let discount: Box<dyn Fn(f64) -> f64> = if use_premium {
 Box::new(|p| p * 0.70)   // 30% off
} else {
 Box::new(|p| p * 0.95)   // 5% off
};
println!("${:.2}", discount(200.0)); // $140.00

// Composable validation strategies โ€” add_rule chains multiple strategies
struct Validator<T> {
 rules: Vec<Box<dyn Fn(&T) -> bool>>,
}
impl<T> Validator<T> {
 fn new() -> Self { Validator { rules: Vec::new() } }
 fn add_rule(mut self, rule: impl Fn(&T) -> bool + 'static) -> Self {
     self.rules.push(Box::new(rule));
     self
 }
 fn validate(&self, value: &T) -> bool {
     self.rules.iter().all(|rule| rule(value))  // ALL rules must pass
 }
}

let validator = Validator::new()
 .add_rule(|&x: &i32| x > 0)
 .add_rule(|&x| x < 1000)
 .add_rule(|&x| x % 2 == 0);
println!("{}", validator.validate(&42));   // true
println!("{}", validator.validate(&1001)); // false (> 1000)

What This Unlocks

Key Differences

ConceptOCamlRust
StrategyHigher-order function`F: Fn(T) -> U` or `Box<dyn Fn>`
Struct with strategy`{ strategy: int -> int }``struct S { f: Box<dyn Fn(i32) -> i32> }`
Runtime swapPass a different functionReplace `Box<dyn Fn>` field value
Multiple strategies`('a -> 'b) list``Vec<Box<dyn Fn(A) -> B>>`
Interface requirementModules / functorsNone โ€” closure type serves as interface
//! # 513. Strategy Pattern via Closures
//! Interchangeable algorithms as closure parameters and struct fields.

/// Sorter with configurable comparison strategy
struct Sorter<T> {
    compare: Box<dyn Fn(&T, &T) -> std::cmp::Ordering>,
}

impl<T: Clone> Sorter<T> {
    fn new(compare: impl Fn(&T, &T) -> std::cmp::Ordering + 'static) -> Self {
        Sorter { compare: Box::new(compare) }
    }

    fn sort(&self, mut data: Vec<T>) -> Vec<T> {
        data.sort_by(|a, b| (self.compare)(a, b));
        data
    }
}

/// Pricing with configurable discount strategy
struct PriceCalculator {
    discount: Box<dyn Fn(f64) -> f64>,
}

impl PriceCalculator {
    fn new(discount: impl Fn(f64) -> f64 + 'static) -> Self {
        PriceCalculator { discount: Box::new(discount) }
    }

    fn calculate(&self, base_price: f64) -> f64 {
        (self.discount)(base_price)
    }
}

/// Validator with composable strategies
struct Validator<T> {
    rules: Vec<Box<dyn Fn(&T) -> bool>>,
}

impl<T> Validator<T> {
    fn new() -> Self { Validator { rules: Vec::new() } }

    fn add_rule(mut self, rule: impl Fn(&T) -> bool + 'static) -> Self {
        self.rules.push(Box::new(rule));
        self
    }

    fn validate(&self, value: &T) -> bool {
        self.rules.iter().all(|rule| rule(value))
    }
}

fn main() {
    // Sorting strategies
    let nums = vec![3, 1, 4, 1, 5, 9, 2, 6];

    let asc_sorter = Sorter::new(|a: &i32, b: &i32| a.cmp(b));
    let desc_sorter = Sorter::new(|a: &i32, b: &i32| b.cmp(a));
    let abs_sorter = Sorter::new(|a: &i32, b: &i32| a.abs().cmp(&b.abs()));

    println!("asc:  {:?}", asc_sorter.sort(nums.clone()));
    println!("desc: {:?}", desc_sorter.sort(nums.clone()));
    println!("abs:  {:?}", abs_sorter.sort(nums));

    // Discount strategies at runtime
    let strategies: Vec<(&str, Box<dyn Fn(f64) -> f64>)> = vec![
        ("no discount",    Box::new(|p| p)),
        ("10% off",        Box::new(|p| p * 0.9)),
        ("bulk (15% off)", Box::new(|p| p * 0.85)),
        ("flat -20",       Box::new(|p| (p - 20.0).max(0.0))),
    ];

    println!("\nPrice for $100.00:");
    for (name, strategy) in &strategies {
        println!("  {}: ${:.2}", name, strategy(100.0));
    }

    // Composable validation strategies
    let validator = Validator::new()
        .add_rule(|&x: &i32| x > 0)
        .add_rule(|&x| x < 1000)
        .add_rule(|&x| x % 2 == 0);

    println!("\nValidation:");
    for n in [42, -1, 1001, 100, 7] {
        println!("  validate({}) = {}", n, validator.validate(&n));
    }

    // Strategy swap at runtime
    let use_premium = true;
    let discount: Box<dyn Fn(f64) -> f64> = if use_premium {
        Box::new(|p| p * 0.7) // 30% off
    } else {
        Box::new(|p| p * 0.95) // 5% off
    };
    println!("\nRuntime strategy: ${:.2} -> ${:.2}", 200.0, discount(200.0));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sorter_asc() {
        let s = Sorter::new(|a: &i32, b: &i32| a.cmp(b));
        assert_eq!(s.sort(vec![3, 1, 2]), vec![1, 2, 3]);
    }

    #[test]
    fn test_sorter_desc() {
        let s = Sorter::new(|a: &i32, b: &i32| b.cmp(a));
        assert_eq!(s.sort(vec![3, 1, 2]), vec![3, 2, 1]);
    }

    #[test]
    fn test_validator() {
        let v = Validator::new()
            .add_rule(|&x: &i32| x > 0)
            .add_rule(|&x| x < 100);
        assert!(v.validate(&50));
        assert!(!v.validate(&0));
        assert!(!v.validate(&100));
    }
}
(* Strategy pattern via higher-order functions in OCaml *)

(* Sort with a custom comparison strategy *)
let sort_with strategy lst =
  List.sort strategy lst

(* Discount calculation strategies *)
let no_discount price = price
let ten_percent price = price *. 0.9
let bulk_discount qty price = if qty >= 10 then price *. 0.85 else price

(* Validation strategies *)
let validate_all validators value =
  List.for_all (fun v -> v value) validators

let () =
  (* Sort strategies *)
  let nums = [3; 1; 4; 1; 5; 9; 2; 6] in
  let asc  = sort_with compare nums in
  let desc = sort_with (fun a b -> compare b a) nums in
  Printf.printf "asc:  [%s]\n" (String.concat ";" (List.map string_of_int asc));
  Printf.printf "desc: [%s]\n" (String.concat ";" (List.map string_of_int desc));

  (* Discount strategies *)
  let strategies = [
    ("none",    no_discount);
    ("10%%",    ten_percent);
    ("bulk(12)",bulk_discount 12);
  ] in
  List.iter (fun (name, strat) ->
    Printf.printf "%s: %.2f\n" name (strat 100.0)
  ) strategies;

  (* Composite validation *)
  let validators = [
    (fun x -> x > 0);
    (fun x -> x < 1000);
    (fun x -> x mod 2 = 0);
  ] in
  Printf.printf "validate 42: %b\n" (validate_all validators 42);
  Printf.printf "validate -1: %b\n" (validate_all validators (-1))