//! # 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