๐Ÿฆ€ 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

120: Fn, FnMut, FnOnce

Difficulty: 3 Level: Intermediate Rust's three closure traits encode how a closure uses its captured variables โ€” the compiler picks the right one automatically.

The Problem This Solves

Every closure captures some variables from its environment. The key question is: what does it do with them? Does it just read them? Mutate them? Consume them outright? The answer determines what the closure is allowed to do โ€” and how safely you can call it multiple times. OCaml has one closure type and no way to express these distinctions in the type system. Rust encodes them as three traits: `Fn`, `FnMut`, and `FnOnce`. A closure that only reads its captures implements all three โ€” it's the most flexible. A closure that mutates its captures implements only `FnMut` and `FnOnce`. A closure that moves a value out of its captures implements only `FnOnce` โ€” you can call it exactly once, because after that call, the captured value is gone. This matters when writing higher-order functions. If you write `fn apply(f: impl Fn())`, callers know you'll invoke `f` multiple times and it's safe. If you write `fn apply(f: impl FnOnce())`, you're telling callers: "I'll call this exactly once, and it can destroy its captures when it does." The type system makes the contract explicit.

The Intuition

`FnOnce` = can call once (moves captured values). `FnMut` = can call multiple times with mutation. `Fn` = can call multiple times, read-only. Every closure is at least `FnOnce`; the most permissive (and common) is `Fn`.

How It Works in Rust

// Fn โ€” captures by shared reference, callable any number of times
fn make_greeter(prefix: String) -> impl Fn(&str) -> String {
 move |name| format!("{}, {}!", prefix, name)
 // `prefix` is moved in, but the closure only reads it โ†’ Fn
}
let greet = make_greeter("Hello".into());
greet("Alice");  // works
greet("Bob");    // works again โ€” Fn means unlimited calls

// FnMut โ€” mutates a captured value, callable multiple times
fn make_counter() -> impl FnMut() -> i32 {
 let mut count = 0;
 move || { count += 1; count }
 // `count` is mutated each call โ†’ FnMut (not Fn)
}
let mut next = make_counter();
assert_eq!(next(), 1);
assert_eq!(next(), 2);

// FnOnce โ€” moves a value out of the capture, callable exactly once
fn make_farewell(name: String) -> impl FnOnce() -> String {
 move || format!("Goodbye, {}!", name)
 // `name` is moved into the return value โ†’ FnOnce
}
let farewell = make_farewell("World".into());
let msg = farewell();  // name is consumed here
// farewell();         // ERROR: use of moved value

// Every Fn is also FnMut and FnOnce โ€” the hierarchy is Fn โŠ‚ FnMut โŠ‚ FnOnce
fn takes_fnonce(f: impl FnOnce() -> i32) -> i32 { f() }
let f = || 42;         // implements Fn
takes_fnonce(f);       // works โ€” Fn satisfies FnOnce too

What This Unlocks

Key Differences

ConceptOCamlRust
Closure traitsOne closure typeThree: `Fn`, `FnMut`, `FnOnce`
Mutating captured stateVia `ref` cell inside the closure`FnMut` โ€” mutable borrow of captured binding
Consuming a captured valueNot expressible`FnOnce` โ€” moves out of captured binding
Callability guaranteesNone in the type`Fn` = unlimited, `FnOnce` = exactly once
// 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"

๐Ÿ“Š Detailed Comparison

Comparison: Closure Traits

Pure / Fn

OCaml:

๐Ÿช Show OCaml equivalent
let make_greeter prefix = fun name -> prefix ^ ", " ^ name ^ "!"
let greet = make_greeter "Hello" in
greet "Alice"; greet "Bob"  (* always reusable *)

Rust:

fn make_greeter(prefix: String) -> impl Fn(&str) -> String {
 move |name| format!("{}, {}!", prefix, name)
}
let greet = make_greeter("Hello".into());
greet("Alice"); greet("Bob");  // Fn = reusable

Stateful / FnMut

OCaml:

๐Ÿช Show OCaml equivalent
let make_counter () =
let count = ref 0 in
fun () -> incr count; !count

Rust:

fn make_counter() -> impl FnMut() -> i32 {
 let mut count = 0;
 move || { count += 1; count }
}

Consuming / FnOnce

OCaml โ€” no equivalent (all closures reusable):

๐Ÿช Show OCaml equivalent
let f = fun () -> message  (* always callable *)

Rust:

fn make_farewell(name: String) -> impl FnOnce() -> String {
 move || format!("Goodbye, {}!", name)  // name consumed
}
let f = make_farewell("World".into());
f();   // OK
// f(); // ERROR: FnOnce already called