//! # 502. Fn, FnMut, FnOnce Hierarchy
//! Demonstrates the three closure trait bounds and their relationships.
/// Accepts any callable โ even one-shot closures
/// FnOnce is the most permissive bound (all closures satisfy it)
fn call_once<F: FnOnce() -> String>(f: F) -> String {
f()
}
/// Accepts callables that can be invoked multiple times with state
fn call_three_times<F: FnMut() -> i32>(mut f: F) -> Vec<i32> {
vec![f(), f(), f()]
}
/// Accepts only pure, non-mutating callables
/// Can be called from multiple threads safely
fn apply_twice<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(f(x))
}
/// Demonstrate FnOnce: consumes a captured value
fn make_greeting(name: String) -> impl FnOnce() -> String {
move || format!("Hello, {}!", name) // name is consumed on call
}
/// Demonstrate FnMut: maintains counter state
fn make_counter() -> impl FnMut() -> i32 {
let mut count = 0;
move || {
count += 1;
count
}
}
/// Demonstrate Fn: pure transformation
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor
}
/// A generic "call N times" โ works with FnMut
fn repeat<F: FnMut() -> T, T>(mut f: F, n: usize) -> Vec<T> {
(0..n).map(|_| f()).collect()
}
fn main() {
// FnOnce example
println!("=== FnOnce ===");
let greet = make_greeting(String::from("Alice"));
println!("{}", call_once(greet));
// greet is consumed โ can't use it again
// FnMut example
println!("\n=== FnMut ===");
let counter = make_counter();
let counts = repeat(counter, 5);
println!("Counts: {:?}", counts);
let mut stateful_counter = make_counter();
let results = call_three_times(&mut stateful_counter);
println!("call_three_times: {:?}", results);
// Fn example
println!("\n=== Fn ===");
let double = make_multiplier(2);
println!("apply_twice(double, 3) = {}", apply_twice(&double, 3)); // 12
println!("apply_twice(double, 5) = {}", apply_twice(&double, 5)); // 20
// double is still usable because Fn doesn't consume
// Hierarchy demonstration: Fn satisfies FnMut and FnOnce
println!("\n=== Hierarchy: Fn satisfies FnMut and FnOnce ===");
let pure_const = || 42i32;
// pure_const implements Fn, FnMut, AND FnOnce
let _ = call_three_times(pure_const); // FnMut bound โ works!
call_once(|| String::from("done")); // FnOnce bound โ works!
println!("Fn closures satisfy all three trait bounds");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fn_once() {
let s = String::from("test");
let f = move || s.to_uppercase();
assert_eq!(call_once(f), "TEST");
}
#[test]
fn test_fn_mut_counter() {
let mut c = make_counter();
assert_eq!(c(), 1);
assert_eq!(c(), 2);
assert_eq!(c(), 3);
}
#[test]
fn test_fn_pure() {
let triple = make_multiplier(3);
assert_eq!(apply_twice(&triple, 2), 18); // triple(triple(2)) = triple(6) = 18
}
#[test]
fn test_repeat() {
let mut n = 0;
let results = repeat(|| { n += 1; n }, 4);
assert_eq!(results, vec![1, 2, 3, 4]);
}
#[test]
fn test_hierarchy() {
// A pure Fn closure can be used wherever FnMut or FnOnce is expected
let g = || 42i32;
assert_eq!(call_three_times(g), vec![42, 42, 42]);
// Fn satisfies FnOnce too:
let result = call_once(|| String::from("from Fn"));
assert_eq!(result, "from Fn");
}
}
(* OCaml has no enforcement โ all functions are just values *)
(* But we can simulate the concepts *)
(* "FnOnce" โ consume a resource on first call *)
let make_once_consumer value =
let used = ref false in
fun () ->
if !used then failwith "already consumed!"
else begin used := true; value end
(* "FnMut" โ maintains mutable state across calls *)
let make_counter () =
let count = ref 0 in
fun () -> incr count; !count
(* "Fn" โ pure, no state *)
let make_adder n = fun x -> x + n
(* Higher-order: call a function multiple times *)
let call_n_times f n =
for _ = 1 to n do f () done
let () =
(* Once-consumer *)
let consume = make_once_consumer "treasure" in
Printf.printf "First call: %s\n" (consume ());
(try let _ = consume () in () with Failure msg -> Printf.printf "Error: %s\n" msg);
(* Counter (FnMut analog) *)
let counter = make_counter () in
call_n_times (fun () ->
Printf.printf "count: %d\n" (counter ())
) 3;
(* Adder (Fn analog) *)
let add5 = make_adder 5 in
Printf.printf "add5(3) = %d\n" (add5 3);
Printf.printf "add5(10) = %d\n" (add5 10)