🦀 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

505: Boxing Closures — Box\<dyn Fn\>

Difficulty: 3 Level: Intermediate Erase closure types to store them in collections, struct fields, and return positions that need runtime flexibility.

The Problem This Solves

Each closure in Rust has a unique, anonymous type. `|x| x + 1` and `|x| x * 2` are different types even though they have the same signature. This means you can't put them in a `Vec` together, store different closures in a struct field without a generic parameter, or return different closures from different branches of a `match`. Generics solve part of this — `struct Processor<F: Fn(i32) -> i32> { f: F }` works if you always use the same closure. But it doesn't scale: you can't have a `Vec<Processor<???>>` where each element has a different closure. You need type erasure. `Box<dyn Fn(i32) -> i32>` is the answer. It heap-allocates the closure and stores a fat pointer (data + vtable). All boxed closures with the same signature are the same type — they can live in the same `Vec`, struct field, or `HashMap`.

The Intuition

`Box<dyn Fn>` is Rust's equivalent of a first-class function value in functional languages. In OCaml, every function value already is a heap-allocated closure. In Rust, you make this explicit. Think of it as sealing the closure in an envelope. The envelope has a standard size and a label (`Fn(i32) -> i32`). You can sort envelopes without caring what's inside. The cost is that calling the function requires opening the envelope (vtable lookup) rather than jumping directly. Python and JavaScript have this automatically — all functions are objects on the heap. Rust makes the heap allocation visible via `Box`, and the dynamic dispatch visible via `dyn`.

How It Works in Rust

// Build a Vec of heterogeneous closures — all behind Box<dyn Fn>
let transforms: Vec<Box<dyn Fn(i32) -> i32>> = vec![
 Box::new(|x| x + 1),     // closure type A
 Box::new(|x| x * 2),     // closure type B (different!)
 Box::new(|x| x * x),     // closure type C
];
// Fold: apply each transform left-to-right
let result = transforms.iter().fold(3, |acc, f| f(acc));
// (3+1)*2 = 8, 8*8 = 64

// Struct with a boxed closure field (no generic parameter needed)
struct Handler {
 name: &'static str,
 apply: Box<dyn Fn(i32) -> String>,
}
impl Handler {
 fn new(name: &'static str, f: impl Fn(i32) -> String + 'static) -> Self {
     Handler { name, apply: Box::new(f) }  // 'static: closure owns all captures
 }
}

// Factory returning different closure types from branches
fn get_transform(mode: &str) -> Box<dyn Fn(i32) -> i32> {
 match mode {
     "double" => Box::new(|x| x * 2),
     "square" => Box::new(|x| x * x),
     _        => Box::new(|x| x),
 }
}
// Caller doesn't know (or care) which branch was taken
let f = get_transform("square");
println!("{}", f(5)); // 25
The `+ 'static` bound means the closure must own all its captured data (no borrowed references) — required when the `Box` outlives the creation scope.

What This Unlocks

Key Differences

ConceptOCamlRust
First-class function valueBuilt-in `'a -> 'b` (always heap)`Box<dyn Fn(A) -> B>` (explicit heap)
Vec of closures`(int -> int) list``Vec<Box<dyn Fn(i32) -> i32>>`
Struct with closure`{ f: int -> int }``struct S { f: Box<dyn Fn(i32) -> i32> }`
Dispatch costAlways vtable (OCaml's representation)Opt-in: `Box<dyn>` vs zero-cost `impl`
Type erasureAutomaticExplicit `Box<dyn ...>`
//! # 505. Boxing Closures — Box<dyn Fn>
//! Type erasure via Box<dyn Fn> for heterogeneous closure collections.

type Transform = Box<dyn Fn(i32) -> i32>;
type Formatter = Box<dyn Fn(i32) -> String>;

/// Build a pipeline of transformations applied left-to-right
fn build_pipeline(transforms: Vec<Transform>) -> impl Fn(i32) -> i32 {
    move |x| transforms.iter().fold(x, |acc, f| f(acc))
}

/// Struct with boxed closure field — no generic parameter needed
struct Handler {
    name: &'static str,
    apply: Formatter,
}

impl Handler {
    fn new(name: &'static str, f: impl Fn(i32) -> String + 'static) -> Self {
        Handler { name, apply: Box::new(f) }
    }

    fn run(&self, x: i32) -> String {
        (self.apply)(x)
    }
}

/// Factory: returns different closures based on runtime value
fn get_transform(mode: &str) -> Box<dyn Fn(i32) -> i32> {
    match mode {
        "double" => Box::new(|x| x * 2),
        "triple" => Box::new(|x| x * 3),
        "square" => Box::new(|x| x * x),
        "negate" => Box::new(|x| -x),
        _         => Box::new(|x| x),
    }
}

/// Heterogeneous closure collection as type-erased Vec
fn make_handlers() -> Vec<Handler> {
    vec![
        Handler::new("stringify", |x| x.to_string()),
        Handler::new("hex",       |x| format!("0x{:x}", x)),
        Handler::new("neg_str",   |x| format!("{}", -x)),
    ]
}

fn main() {
    // Heterogeneous vec of transforms
    let transforms: Vec<Transform> = vec![
        Box::new(|x| x + 1),
        Box::new(|x| x * 2),
        Box::new(|x| x * x),
        Box::new(|x| -x),
    ];

    let pipeline = build_pipeline(transforms);
    println!("pipeline(3) = {}", pipeline(3)); // ((3+1)*2)^2 * -1

    // Dynamic dispatch based on runtime mode
    let f = get_transform("double");
    let g = get_transform("square");
    println!("double(7) = {}", f(7));
    println!("square(5) = {}", g(5));

    // Struct with boxed closure
    let handlers = make_handlers();
    for h in &handlers {
        println!("{}(42) = {}", h.name, h.run(42));
    }

    // Store in a map-like structure
    let mut dispatch: std::collections::HashMap<&str, Box<dyn Fn(i32) -> i32>> =
        std::collections::HashMap::new();
    dispatch.insert("add10", Box::new(|x| x + 10));
    dispatch.insert("mul5",  Box::new(|x| x * 5));

    if let Some(f) = dispatch.get("add10") {
        println!("add10(3) = {}", f(3));
    }
}

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

    #[test]
    fn test_pipeline() {
        let transforms: Vec<Transform> = vec![
            Box::new(|x| x + 1),
            Box::new(|x| x * 2),
        ];
        let p = build_pipeline(transforms);
        assert_eq!(p(4), 10); // (4+1)*2
    }

    #[test]
    fn test_get_transform() {
        assert_eq!(get_transform("double")(5), 10);
        assert_eq!(get_transform("triple")(3), 9);
        assert_eq!(get_transform("negate")(7), -7);
        assert_eq!(get_transform("other")(8), 8);
    }

    #[test]
    fn test_handler() {
        let h = Handler::new("double_str", |x| format!("{}", x * 2));
        assert_eq!(h.run(5), "10");
    }

    #[test]
    fn test_heterogeneous_vec() {
        let transforms: Vec<Box<dyn Fn(i32) -> i32>> = vec![
            Box::new(|x| x + 1),
            Box::new(|x| x * 2),
            Box::new(|x| x - 3),
        ];
        let result = transforms.iter().fold(5, |acc, f| f(acc));
        assert_eq!(result, 9); // (5+1)*2-3
    }
}
(* OCaml: functions are already first-class with uniform representation *)

(* Heterogeneous list of functions (all same type 'int -> int') *)
let int_transforms : (int -> int) list = [
  (fun x -> x + 1);
  (fun x -> x * 2);
  (fun x -> x * x);
  (fun x -> -x);
]

(* Apply a pipeline of transforms *)
let pipeline transforms x =
  List.fold_left (fun acc f -> f acc) x transforms

(* Struct-like record with function field *)
type handler = {
  name: string;
  apply: int -> string;
}

let make_handler name f = { name; apply = f }

let () =
  (* Pipeline *)
  let result = pipeline int_transforms 3 in
  Printf.printf "pipeline(3) = %d\n" result;

  (* Conditional function selection *)
  let get_transform mode =
    match mode with
    | "double" -> (fun x -> x * 2)
    | "triple" -> (fun x -> x * 3)
    | _ -> (fun x -> x)
  in
  let f = get_transform "double" in
  Printf.printf "double(7) = %d\n" (f 7);

  (* Record with function *)
  let handlers = [
    make_handler "stringify" (fun x -> string_of_int x);
    make_handler "hex" (fun x -> Printf.sprintf "0x%x" x);
    make_handler "neg_str" (fun x -> string_of_int (-x));
  ] in
  List.iter (fun h ->
    Printf.printf "%s(42) = %s\n" h.name (h.apply 42)
  ) handlers