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

526: Pipe Operator Simulation

Difficulty: 2 Level: Beginner-Intermediate Simulate OCaml's `|>` operator in Rust โ€” left-to-right function application that reads like a sentence.

The Problem This Solves

Nested function calls read inside-out: `format_output(normalize(parse(input)))` โ€” you have to read right-to-left to understand the order of operations. With three transformations it's manageable; with five it's a puzzle. Rust's method chaining solves this for types that own their methods. But what about free functions? `parse(input)` followed by `normalize(...)` followed by `format_output(...)` โ€” there are no methods to chain because these aren't methods. Also: sometimes you want to apply a function from an external crate, pass through a generic utility, or change the type at each step. None of these fit method chaining. You need a way to write free-function pipelines left-to-right.

The Intuition

OCaml's `|>` pipe operator is just function application flipped: `x |> f` means `f(x)`. The power comes from chaining: `x |> f |> g |> h` means `h(g(f(x)))` โ€” but reads left-to-right like a recipe: "start with x, apply f, then g, then h." Elixir, F#, and Haskell all have this operator. Rust's RFC for `|>` was declined (method chaining is preferred). But a `Pipe` extension trait achieves the same result: `x.pipe(f).pipe(g).pipe(h)`. Think of it as reversing the function call: instead of `f(x)`, you write `x.pipe(f)`. Now the data flows left-to-right, and each transformation step is equally visible.

How It Works in Rust

// Extension trait: adds .pipe() to every type
trait Pipe: Sized {
 fn pipe<B, F: FnOnce(Self) -> B>(self, f: F) -> B {
     f(self)    // simply calls f with self โ€” all the magic is in the type
 }
 fn pipe_ref<B, F: FnOnce(&Self) -> B>(&self, f: F) -> B {
     f(self)    // non-consuming: self remains usable after
 }
 fn pipe_mut<B, F: FnOnce(&mut Self) -> B>(&mut self, f: F) -> B {
     f(self)
 }
}
impl<T> Pipe for T {}  // blanket impl: works on all types

fn double(x: i32) -> i32 { x * 2 }
fn add1(x: i32) -> i32 { x + 1 }
fn square(x: i32) -> i32 { x * x }

// Without pipe: right-to-left reading (backward)
let r1 = square(add1(double(3)));   // read: square of (add1 of (double of 3))

// With pipe: left-to-right reading (natural)
let r2 = 3i32.pipe(double).pipe(add1).pipe(square);  // 3 โ†’ 6 โ†’ 7 โ†’ 49

// Type changes work naturally
let result = 42i32
 .pipe(|x| x.to_string())           // i32 โ†’ String
 .pipe(|s| format!("value={}", s))  // String โ†’ String
 .pipe(|s| s.to_uppercase());       // String โ†’ String

// Closures capture context
let offset = 10;
let result2 = 5i32
 .pipe(|x| x + offset)    // captures offset
 .pipe(|x| x * 3)
 .pipe(|x| x.to_string());  // "45"

// Non-consuming pipe_ref โ€” data stays owned
let data = vec![5, 3, 8, 1, 9, 2];
let max = data.pipe_ref(|v| v.iter().max().copied()); // data still owned
println!("{:?}", data); // still usable

// Complex pipeline with type changes at each step
let word_lengths: Vec<usize> = "hello world rust"
 .pipe(|s| s.split_whitespace().collect::<Vec<_>>())  // &str โ†’ Vec<&str>
 .pipe(|words| words.iter().map(|w| w.len()).collect()); // โ†’ Vec<usize>

What This Unlocks

Key Differences

ConceptOCamlRust
Pipe operator`x \> f \> g` โ€” built-in syntax`x.pipe(f).pipe(g)` โ€” extension trait
AvailabilityAlways availableImport the `Pipe` trait
Type change`x \> (fun x -> x + 1)` โ€” natural`x.pipe(\x\x + 1)` โ€” same
Consume vs borrow`\>` always consumes`.pipe()` consumes; `.pipe_ref()` borrows
Point-free style`let f = g \> h`Composable with closures
//! # 526. Pipe Operator Simulation
//! Simulating OCaml's |> operator with a Pipe extension trait.

/// Extension trait to simulate the |> pipe operator
trait Pipe: Sized {
    /// Apply f to self, left-to-right: self.pipe(f) == f(self)
    fn pipe<B, F: FnOnce(Self) -> B>(self, f: F) -> B {
        f(self)
    }

    /// pipe_ref: apply f to &self (doesn't consume)
    fn pipe_ref<B, F: FnOnce(&Self) -> B>(&self, f: F) -> B {
        f(self)
    }

    /// pipe_mut: apply f to &mut self
    fn pipe_mut<B, F: FnOnce(&mut Self) -> B>(&mut self, f: F) -> B {
        f(self)
    }
}

impl<T> Pipe for T {}

fn double(x: i32) -> i32 { x * 2 }
fn add1(x: i32) -> i32 { x + 1 }
fn square(x: i32) -> i32 { x * x }

fn main() {
    // Basic pipe: 3 |> double |> add1 |> square
    let result = 3i32
        .pipe(double)
        .pipe(add1)
        .pipe(square);
    println!("3 |> double |> add1 |> square = {}", result); // (3*2+1)^2 = 49

    // Pipe with closures
    let offset = 10;
    let result2 = 5i32
        .pipe(|x| x + offset)
        .pipe(|x| x * 3)
        .pipe(|x| x.to_string());
    println!("5 + 10 * 3 as string = {:?}", result2);

    // Pipe with Vec transformations
    let sum_evens_tripled = vec![1, 2, 3, 4, 5, 6]
        .pipe(|v| v.into_iter().filter(|x| x % 2 == 0).collect::<Vec<_>>())
        .pipe(|v| v.into_iter().map(|x| x * 3).collect::<Vec<_>>())
        .pipe(|v| v.into_iter().sum::<i32>());
    println!("sum of tripled evens: {}", sum_evens_tripled);

    // pipe_ref for non-consuming inspection
    let data = vec![5, 3, 8, 1, 9, 2];
    let max = data
        .pipe_ref(|v| v.iter().max().copied());
    println!("max: {:?}", max);
    println!("data still owned: {:?}", data); // data not consumed

    // Multi-type pipe chain
    let word_lengths: Vec<usize> = "hello world rust programming"
        .pipe(|s| s.split_whitespace().collect::<Vec<_>>())
        .pipe(|words| words.iter().map(|w| w.len()).collect());
    println!("word lengths: {:?}", word_lengths);

    // Simulate OCaml pipeline
    let result3 = 42i32
        .pipe(|x| x.to_string())
        .pipe(|s| format!("The answer is: {}", s))
        .pipe(|s| s.to_uppercase());
    println!("{}", result3);
}

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

    #[test]
    fn test_basic_pipe() {
        let result = 3i32.pipe(double).pipe(add1).pipe(square);
        assert_eq!(result, 49);
    }

    #[test]
    fn test_pipe_type_change() {
        let result = 42i32.pipe(|x| x.to_string());
        assert_eq!(result, "42");
    }

    #[test]
    fn test_pipe_ref_no_consume() {
        let v = vec![1, 2, 3];
        let sum = v.pipe_ref(|v| v.iter().sum::<i32>());
        assert_eq!(sum, 6);
        assert_eq!(v, [1, 2, 3]); // not consumed
    }

    #[test]
    fn test_pipe_closure_capture() {
        let n = 100;
        let result = 5i32.pipe(move |x| x + n);
        assert_eq!(result, 105);
    }
}
(* Pipe operator in OCaml โ€” built in as |> *)
let double x = x * 2
let add1 x = x + 1
let square x = x * x
let to_string x = string_of_int x

let () =
  let result = 3 |> double |> add1 |> square |> to_string in
  Printf.printf "3 |> double |> add1 |> square |> to_string = %s\n" result;

  (* Pipe with anonymous functions *)
  let processed =
    [1;2;3;4;5]
    |> List.filter (fun x -> x mod 2 = 0)
    |> List.map (fun x -> x * 3)
    |> List.fold_left (+) 0
  in
  Printf.printf "sum of tripled evens: %d\n" processed