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

508: Partial Application with Closures

Difficulty: 2 Level: Beginner-Intermediate Fix some arguments of a function, producing a specialized version with fewer parameters.

The Problem This Solves

You have a general function `clamp(lo, hi, x)` and you need to call it hundreds of times always with `lo=0, hi=100`. Every call site repeats `clamp(0, 100, value)`. If the constants change, you update dozens of places. Or you're building a pipeline: `items.iter().filter(|x| in_range(0, 100, x))` โ€” the predicate logic is there but the bounds are scattered. What you really want is a pre-configured `in_range_0_100` you can pass around cleanly. This is the problem partial application solves: fix the "configuration" arguments, get back a function that only takes the "input" arguments. It's a core technique in functional programming that dramatically reduces repetition and makes code read at the right level of abstraction.

The Intuition

Partial application is like filling out a form ahead of time. `send_email(from, to, subject, body)` โ€” you fill in `from` and `subject` once at the top of your module, and pass around `send_with_context(to, body)` to everyone who needs to send emails. In Python: `from functools import partial; add5 = partial(add, 5)`. In JavaScript: `const add5 = (x) => add(5, x)` or `add.bind(null, 5)`. Rust uses the same technique as JavaScript: a closure that captures the fixed arguments via `move` and forwards them to the original function. Unlike OCaml or Haskell where functions are curried by default (applying fewer args than expected automatically produces a partial application), Rust requires you to write the closure explicitly โ€” but it's one line.

How It Works in Rust

fn clamp(lo: i32, hi: i32, x: i32) -> i32 { x.max(lo).min(hi) }
fn between(lo: i32, hi: i32, x: i32) -> bool { x >= lo && x <= hi }

// Manual partial application: capture fixed args via move closure
let clamp_0_100 = |x| clamp(0, 100, x);   // lo and hi are fixed
let in_teens    = |x| between(13, 19, x); // lo and hi are fixed

println!("{}", clamp_0_100(150));  // 100
println!("{}", in_teens(15));      // true

// Generic partial helper: fix the first argument of any 2-arg function
fn partial<A: Copy, B, C, F>(f: F, a: A) -> impl Fn(B) -> C
where F: Fn(A, B) -> C {
 move |b| f(a, b)   // a is captured by value (Copy), b comes from caller
}

let starts_with_hello = partial(|prefix: &str, s: &str| s.starts_with(prefix), "hello");
println!("{}", starts_with_hello("hello world")); // true
println!("{}", starts_with_hello("hi there"));    // false

// In a pipeline: partially applied functions slot right into iterators
let add5 = |x: &i32| x + 5;       // fixes the +5 part
let double = |x: i32| x * 2;      // captures nothing; still "partially applies" the *2

let result: Vec<i32> = [1, 2, 3, 4, 5].iter()
 .map(add5)
 .map(double)
 .filter(|&x| between(13, 19, x))  // or .filter(in_teens.clone())
 .collect();

What This Unlocks

Key Differences

ConceptOCamlRust
Partial application`let add5 = (+) 5` โ€” automatic`let add5 = \x\add(5, x)` โ€” explicit closure
Curried by defaultYes โ€” `f a b = (f a) b`No โ€” must wrap in closure
Fix first arg`let f = g arg1``move \rest\g(arg1, rest)`
Generic helper`let partial f a b = f a b``fn partial<A,B,C,F>(f: F, a: A) -> impl Fn(B)->C`
Iterator integrationNatural with curried predicatesClosures plug directly into `map`/`filter`
//! # 508. Partial Application with Closures
//! Simulating partial application by capturing fixed arguments in closures.

/// Full function: add two numbers
fn add(x: i32, y: i32) -> i32 { x + y }

/// Full function: clamp x into [lo, hi]
fn clamp(lo: i32, hi: i32, x: i32) -> i32 { x.max(lo).min(hi) }

/// Full function: check if x is in [lo, hi]
fn between(lo: i32, hi: i32, x: i32) -> bool { x >= lo && x <= hi }

/// Generic partial application: fix the first argument of a 2-arg function
fn partial<A: Copy, B, C, F>(f: F, a: A) -> impl Fn(B) -> C
where
    F: Fn(A, B) -> C,
{
    move |b| f(a, b)
}

/// Partial with two fixed args (fix lo and hi of a 3-arg function)
fn partial2<A: Copy, B: Copy, C, D, F>(f: F, a: A, b: B) -> impl Fn(C) -> D
where
    F: Fn(A, B, C) -> D,
{
    move |c| f(a, b, c)
}

fn main() {
    // Manual partial application via closures
    let add5 = |y| add(5, y);
    let double = |y| add(y, y); // or multiply(2, y)
    let clamp_0_100 = |x| clamp(0, 100, x);
    let in_teens = |x| between(13, 19, x);

    println!("add5(10) = {}", add5(10));
    println!("double(7) = {}", double(7));
    println!("clamp(150) = {}", clamp_0_100(150));
    println!("clamp(-5) = {}", clamp_0_100(-5));
    println!("in_teens(15) = {}", in_teens(15));
    println!("in_teens(20) = {}", in_teens(20));

    // Generic partial helper
    let times3 = partial(|x: i32, y: i32| x * y, 3);
    println!("times3(8) = {}", times3(8));

    let starts_with_hello = partial(|prefix: &str, s: &str| s.starts_with(prefix), "hello");
    println!("starts_with_hello(\"hello world\") = {}", starts_with_hello("hello world"));
    println!("starts_with_hello(\"hi there\") = {}", starts_with_hello("hi there"));

    // Partial2: fix lo=0, hi=100
    let clamp_fn = partial2(clamp, 0, 100);
    println!("clamp_fn(42) = {}", clamp_fn(42));
    println!("clamp_fn(200) = {}", clamp_fn(200));

    // In a pipeline
    let items = [1, 2, 3, 4, 5, 6];
    let add5_fn = |x: &i32| add(5, *x);
    let double_fn = |x: i32| add(x, x);
    let in_teens_fn = |x: &i32| between(13, 19, *x);

    let result: Vec<i32> = items.iter()
        .map(add5_fn)
        .map(double_fn)
        .filter(in_teens_fn)
        .collect();
    println!("pipeline result: {:?}", result);
}

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

    #[test]
    fn test_manual_partial() {
        let add10 = |y: i32| add(10, y);
        assert_eq!(add10(5), 15);
        assert_eq!(add10(0), 10);
    }

    #[test]
    fn test_generic_partial() {
        let mul_by_7 = partial(|x: i32, y: i32| x * y, 7);
        assert_eq!(mul_by_7(6), 42);
    }

    #[test]
    fn test_partial2() {
        let check = partial2(between, 5, 10);
        assert!(check(7));
        assert!(!check(11));
    }

    #[test]
    fn test_partial_string() {
        let prefix_checker = partial(|p: &str, s: &str| s.starts_with(p), "rust");
        assert!(prefix_checker("rustacean"));
        assert!(!prefix_checker("python"));
    }
}
(* Partial application in OCaml โ€” natural via currying *)

(* All multi-arg functions are curried by default *)
let add x y = x + y
let multiply x y = x * y
let clamp lo hi x = max lo (min hi x)
let between lo hi x = x >= lo && x <= hi

(* Partial application โ€” just apply some args *)
let add5 = add 5         (* int -> int *)
let double = multiply 2  (* int -> int *)
let clamp_0_100 = clamp 0 100   (* int -> int *)
let in_teens = between 13 19    (* int -> bool *)

(* Partial application in pipelines *)
let process items =
  items
  |> List.map add5
  |> List.map double
  |> List.filter in_teens

(* Generic partial: fix first argument *)
let partial f a = fun b -> f a b

let () =
  Printf.printf "add5(10) = %d\n" (add5 10);
  Printf.printf "double(7) = %d\n" (double 7);
  Printf.printf "clamp(150) = %d\n" (clamp_0_100 150);
  Printf.printf "in_teens(15) = %b\n" (in_teens 15);
  Printf.printf "in_teens(20) = %b\n" (in_teens 20);

  let results = process [1; 2; 3; 4; 5; 6] in
  Printf.printf "processed: [%s]\n" (String.concat ";" (List.map string_of_int results));

  let times3 = partial multiply 3 in
  Printf.printf "times3(8) = %d\n" (times3 8)