πŸ¦€ 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

001: Higher-Order Functions

Difficulty: 1 Level: Beginner Pass functions as arguments, return them as values, and use `map`/`filter`/`fold` β€” the three most useful tools in functional programming.

The Problem This Solves

Imagine you need to double every number in a list, then do the same but triple them, then square them. Without higher-order functions, you write nearly identical loops three times. Change the logic? Edit in three places. That's how bugs are born. Higher-order functions let you write the loop once and pass in what changes: the transformation. `double_all` and `triple_all` become the same function with different arguments. This is the insight behind `map`: apply any function to every element of a list, without writing the loop yourself. The same principle applies to `filter` (keep elements matching a condition) and `fold` (combine elements into a single value). Once you have these three, you can express almost any data transformation as a clean, readable pipeline β€” no imperative loops required.

The Intuition

Python developers already know this:
doubled = [x * 2 for x in numbers]          # map
evens   = [x for x in numbers if x % 2 == 0] # filter
total   = sum(numbers)                         # fold/reduce
JavaScript:
numbers.map(x => x * 2).filter(x => x % 2 === 0).reduce((a, b) => a + b, 0)
Rust looks almost identical to the JS version:
numbers.iter().map(|x| x * 2).filter(|x| x % 2 == 0).sum()
The `|x| x * 2` syntax is a closure β€” Rust's version of an arrow function or lambda. The `||` holds the parameters, and the expression after is the body.

How It Works in Rust

// A function that takes another function as an argument
fn apply<A, B>(f: impl Fn(A) -> B, x: A) -> B {
 f(x)  // just call f with x
}

// Apply a function twice β€” compose with itself
fn twice<A>(f: impl Fn(A) -> A, x: A) -> A {
 f(f(x))
}

// A function that RETURNS a function (closure)
fn adder(n: i32) -> impl Fn(i32) -> i32 {
 move |x| x + n  // `move` captures `n` so the closure can outlive this call
}

let add10 = adder(10);  // add10 is now a function
println!("{}", add10(7)); // 17

// map / filter / fold on slices
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
//                                          ^^^^ destructure the &i32 reference

let evens: Vec<i32> = numbers.iter().copied().filter(|x| x % 2 == 0).collect();
//                              ^^^^^^ turn &i32 into i32 before filtering

let total: i32 = numbers.iter().fold(0, |acc, &x| acc + x);
// fold takes: initial value, then a closure (accumulator, current) -> new accumulator

// All three together: square β†’ keep > 10 β†’ sum
let result = map_filter_fold(
 numbers.iter().copied(),
 |x| x * x,          // map: square
 |x| x > &10,        // filter: keep only > 10
 0,
 |acc, x| acc + x,   // fold: sum
);
// 16 + 25 + 36 + 49 + 64 + 81 + 100 = 371
The `<A, B>` in function signatures are generics β€” they mean "this works for any types A and B." `impl Fn(A) -> B` means "any function (or closure) that takes A and returns B."

What This Unlocks

Key Differences

ConceptOCamlRust
Lambda syntax`fun x -> x + 1``\x\x + 1`
Map a list`List.map f xs``xs.iter().map(f).collect()`
Filter`List.filter pred xs``xs.iter().filter(pred).collect()`
Fold/reduce`List.fold_left f init xs``xs.iter().fold(init, f)`
Function type`int -> int``impl Fn(i32) -> i32`
Return a functionNatural β€” just return a lambda`fn f() -> impl Fn(...)` with `move` closure
//! Higher-Order Functions in Rust
//!
//! Functions that take other functions as arguments, return functions (closures),
//! and compose them β€” the foundation of functional-style programming.
//!
//! OCaml parallel: `apply`, `twice`, `compose`, `|>` operator.

// ── Passing functions as arguments ───────────────────────────────────────────

/// Apply a function to a value. Equivalent to OCaml's `let apply f x = f x`.
fn apply<A, B>(f: impl Fn(A) -> B, x: A) -> B {
    f(x)
}

/// Apply a function to a value twice. OCaml: `let twice f x = f (f x)`.
fn twice<A>(f: impl Fn(A) -> A, x: A) -> A {
    f(f(x))
}

// ── Returning functions (closures) ────────────────────────────────────────────

/// Return a closure that adds `n` to its argument.
fn adder(n: i32) -> impl Fn(i32) -> i32 {
    move |x| x + n
}

/// Return a closure that multiplies its argument by `n`.
fn multiplier(n: i32) -> impl Fn(i32) -> i32 {
    move |x| x * n
}

// ── Function composition ──────────────────────────────────────────────────────

/// Compose two functions: `compose(f, g)(x)` = `f(g(x))`.
/// OCaml: `let compose f g x = f (g x)`.
fn compose<A, B, C>(
    f: impl Fn(B) -> C,
    g: impl Fn(A) -> B,
) -> impl Fn(A) -> C {
    move |x| f(g(x))
}

/// Pipe a value through a slice of `Box<dyn Fn(i32) -> i32>` transformations
/// (a poor-man's `|>` pipeline for homogeneous types).
fn pipe(value: i32, fns: &[&dyn Fn(i32) -> i32]) -> i32 {
    fns.iter().fold(value, |acc, f| f(acc))
}

// ── Iterator combinators (map / filter / fold) ────────────────────────────────

/// Double every number in a slice.
fn double_all(xs: &[i32]) -> Vec<i32> {
    xs.iter().map(|&x| x * 2).collect()
}

/// Keep only the even numbers from a slice.
fn keep_evens(xs: &[i32]) -> Vec<i32> {
    xs.iter().copied().filter(|x| x % 2 == 0).collect()
}

/// Sum all numbers in a slice using `fold`.
fn sum(xs: &[i32]) -> i32 {
    xs.iter().fold(0, |acc, &x| acc + x)
}

/// Generic higher-order transform: map, then filter, then fold β€” all in one pass.
fn map_filter_fold<A, B>(
    xs: impl Iterator<Item = A>,
    map_fn:    impl Fn(A) -> B,
    filter_fn: impl Fn(&B) -> bool,
    init:      B,
    fold_fn:   impl Fn(B, B) -> B,
) -> B {
    xs.map(map_fn).filter(filter_fn).fold(init, fold_fn)
}

// ── Entry point ───────────────────────────────────────────────────────────────

fn main() {
    // apply
    let result = apply(|x| x * 2, 21);
    println!("apply (Γ—2) 21        = {result}");          // 42

    // twice
    let result = twice(|x| x + 1, 5);
    println!("twice (+1) 5         = {result}");          // 7

    // compose: first +1, then Γ—2  β†’  (5+1)*2 = 12
    let double_after_inc = compose(multiplier(2), adder(1));
    println!("compose (Γ—2 ∘ +1) 5  = {}", double_after_inc(5)); // 12

    // pipe  5 β†’ +1 β†’ Γ—2 = 12
    let inc = adder(1);
    let dbl = multiplier(2);
    let result = pipe(5, &[&inc, &dbl]);
    println!("pipe 5 |> +1 |> Γ—2   = {result}");         // 12

    // returned closures
    let add10 = adder(10);
    println!("adder(10)(7)         = {}", add10(7));      // 17

    // iterator combinators
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    println!("double_all [1..10]   = {:?}", double_all(&numbers));
    println!("keep_evens [1..10]   = {:?}", keep_evens(&numbers));
    println!("sum        [1..10]   = {}", sum(&numbers)); // 55

    // generic map→filter→fold: square all, keep >10, sum them
    let total = map_filter_fold(
        numbers.iter().copied(),
        |x| x * x,          // map:    square
        |x| x > &10,        // filter: keep > 10
        0,                   // init
        |acc, x| acc + x,   // fold:   sum
    );
    println!("sum of squares > 10 = {total}");            // 16+25+36+49+64+81+100 = 371
}

// ── Tests ─────────────────────────────────────────────────────────────────────

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

    #[test]
    fn test_apply() {
        assert_eq!(apply(|x: i32| x * 3, 7), 21);
        assert_eq!(apply(|s: &str| s.len(), "hello"), 5);
    }

    #[test]
    fn test_twice() {
        assert_eq!(twice(|x| x + 1, 5), 7);
        assert_eq!(twice(|x: i32| x * 2, 3), 12);
    }

    #[test]
    fn test_compose() {
        let f = compose(|x: i32| x * 2, |x: i32| x + 1);
        assert_eq!(f(5), 12);  // (5+1)*2
        assert_eq!(f(0), 2);   // (0+1)*2
    }

    #[test]
    fn test_adder_and_multiplier() {
        let add5  = adder(5);
        let times3 = multiplier(3);
        assert_eq!(add5(10), 15);
        assert_eq!(times3(4), 12);
    }

    #[test]
    fn test_pipe() {
        let inc = adder(1);
        let dbl = multiplier(2);
        assert_eq!(pipe(5, &[&inc, &dbl]), 12);
        assert_eq!(pipe(0, &[&inc, &dbl]), 2);
        assert_eq!(pipe(5, &[]),           5); // identity
    }

    #[test]
    fn test_iterator_combinators() {
        let xs = vec![1, 2, 3, 4, 5];
        assert_eq!(double_all(&xs), vec![2, 4, 6, 8, 10]);
        assert_eq!(keep_evens(&xs), vec![2, 4]);
        assert_eq!(sum(&xs), 15);
    }

    #[test]
    fn test_map_filter_fold() {
        // square [1..5], keep those > 10, sum β†’ 16+25 = 41
        let result = map_filter_fold(
            1..=5,
            |x| x * x,
            |x| x > &10,
            0,
            |acc, x| acc + x,
        );
        assert_eq!(result, 41);
    }
}
(* Higher-order functions: functions that take or return other functions *)
let apply f x = f x
let twice f x = f (f x)
let compose f g x = f (g x)
let ( |> ) x f = f x

let () =
  Printf.printf "%d\n" (apply (fun x -> x * 2) 21);
  Printf.printf "%d\n" (twice (fun x -> x + 1) 5);
  Printf.printf "%d\n" ((compose (fun x -> x * 2) (fun x -> x + 1)) 5);
  Printf.printf "%d\n" (5 |> (fun x -> x + 1) |> (fun x -> x * 2))