// Example 120: Fn, FnMut, FnOnce
//
// Rust closures implement one or more of three traits:
// - Fn: borrows captured values immutably (can call repeatedly)
// - FnMut: borrows captured values mutably (can call repeatedly)
// - FnOnce: consumes captured values (can call only once)
// Approach 1: Fn โ read-only captures
fn make_greeter(prefix: String) -> impl Fn(&str) -> String {
move |name| format!("{}, {}!", prefix, name)
}
fn apply_fn(f: &dyn Fn(&str) -> String, name: &str) -> String {
f(name)
}
fn approach1() {
let greet = make_greeter("Hello".into());
let r1 = greet("Alice");
let r2 = greet("Bob"); // can call multiple times
assert_eq!(r1, "Hello, Alice!");
assert_eq!(r2, "Hello, Bob!");
// Can pass as &dyn Fn
let r3 = apply_fn(&greet, "Charlie");
println!("{} | {} | {}", r1, r2, r3);
}
// Approach 2: FnMut โ mutable captures
fn make_counter() -> impl FnMut() -> i32 {
let mut count = 0;
move || {
count += 1;
count
}
}
fn call_mut_twice(mut f: impl FnMut() -> i32) -> (i32, i32) {
(f(), f())
}
fn approach2() {
let mut next = make_counter();
assert_eq!(next(), 1);
assert_eq!(next(), 2);
assert_eq!(next(), 3);
let counter2 = make_counter();
let (a, b) = call_mut_twice(counter2);
assert_eq!((a, b), (1, 2));
println!("Counter: 1, 2, 3 โ | Pair: ({}, {})", a, b);
}
// Approach 3: FnOnce โ consumes captures
fn make_farewell(name: String) -> impl FnOnce() -> String {
move || {
// name is moved into the return value โ consumed!
format!("Goodbye, {}!", name)
}
}
fn call_once(f: impl FnOnce() -> String) -> String {
f()
// f() // ERROR: can't call FnOnce twice
}
fn approach3() {
let farewell = make_farewell("World".into());
let msg = call_once(farewell);
// farewell is consumed โ can't call again
assert_eq!(msg, "Goodbye, World!");
println!("{}", msg);
}
fn main() {
println!("=== Approach 1: Fn (Read-Only) ===");
approach1();
println!("\n=== Approach 2: FnMut (Mutable) ===");
approach2();
println!("\n=== Approach 3: FnOnce (Consuming) ===");
approach3();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fn_reusable() {
let f = make_greeter("Hi".into());
assert_eq!(f("A"), "Hi, A!");
assert_eq!(f("B"), "Hi, B!");
}
#[test]
fn test_fnmut_stateful() {
let mut c = make_counter();
assert_eq!(c(), 1);
assert_eq!(c(), 2);
}
#[test]
fn test_fnonce_consumed() {
let f = make_farewell("Test".into());
assert_eq!(call_once(f), "Goodbye, Test!");
}
#[test]
fn test_fn_hierarchy() {
// Every Fn is also FnMut and FnOnce
fn takes_fnonce(f: impl FnOnce() -> i32) -> i32 { f() }
let f = || 42; // implements Fn
assert_eq!(takes_fnonce(f), 42); // works as FnOnce too
}
#[test]
fn test_closure_as_fn_param() {
fn apply(f: impl Fn(i32) -> i32, x: i32) -> i32 { f(x) }
assert_eq!(apply(|x| x * 2, 5), 10);
assert_eq!(apply(|x| x + 1, 5), 6);
}
}
(* Example 120: Fn, FnMut, FnOnce โ OCaml Closures vs Rust Closure Traits *)
(* OCaml has one closure type. Rust has three traits based on how
the closure uses captured variables. *)
(* Approach 1: Pure closure โ Fn (read-only capture) *)
let make_greeter prefix =
fun name -> prefix ^ ", " ^ name ^ "!"
let approach1 () =
let greet = make_greeter "Hello" in
let r1 = greet "Alice" in
let r2 = greet "Bob" in
assert (r1 = "Hello, Alice!");
assert (r2 = "Hello, Bob!");
Printf.printf "%s | %s\n" r1 r2
(* Approach 2: Mutating closure โ FnMut (mutable capture) *)
let make_counter () =
let count = ref 0 in
fun () ->
incr count;
!count
let approach2 () =
let next = make_counter () in
assert (next () = 1);
assert (next () = 2);
assert (next () = 3);
Printf.printf "Counter: 1, 2, 3 โ\n"
(* Approach 3: Consuming closure โ FnOnce (consumes captured value) *)
let consume_and_greet name =
let message = "Goodbye, " ^ name ^ "!" in
fun () -> message (* captures message โ but in OCaml, no "consumption" *)
let approach3 () =
let farewell = consume_and_greet "World" in
let msg = farewell () in
let msg2 = farewell () in (* In OCaml, we can call again *)
assert (msg = msg2);
Printf.printf "%s\n" msg
let () =
approach1 ();
approach2 ();
approach3 ();
Printf.printf "โ All tests passed\n"