๐Ÿฆ€ Functional Rust
๐ŸŽฌ How Rust Iterators Work Lazy evaluation, chaining, collect(), and zero-cost abstractions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Iterators are lazy โ€” .map(), .filter(), .take() build a chain but do no work until consumed

โ€ข .collect() triggers evaluation, transforming the chain into a Vec, HashMap, or other collection

โ€ข Zero-cost abstraction: iterator chains compile to the same machine code as hand-written loops

โ€ข .iter() borrows, .into_iter() consumes, .iter_mut() borrows mutably

โ€ข Chaining replaces nested loops with a readable, composable pipeline

083: Display Trait

Difficulty: Intermediate Category: Traits Concept: Implement `Display` for custom types to enable `format!` and `println!` Key Insight: Rust's `Display` is opt-in (you must implement it); OCaml relies on manual `to_string` functions or `Printf` format strings.
// 083: Display Trait
// Implement Display for custom types

use std::fmt;

// Approach 1: Simple Display
#[derive(Debug)]
enum Color { Red, Green, Blue }

impl fmt::Display for Color {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Color::Red => write!(f, "Red"),
            Color::Green => write!(f, "Green"),
            Color::Blue => write!(f, "Blue"),
        }
    }
}

#[derive(Debug)]
struct Point { x: f64, y: f64 }

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({:.1}, {:.1})", self.x, self.y)
    }
}

// Approach 2: Complex formatting
#[derive(Debug)]
struct Person { name: String, age: u32, email: String }

impl fmt::Display for Person {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} (age {}, {})", self.name, self.age, self.email)
    }
}

// Approach 3: Recursive Display
#[derive(Debug)]
enum Tree<T> { Leaf, Node(Box<Tree<T>>, T, Box<Tree<T>>) }

impl<T: fmt::Display> fmt::Display for Tree<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Tree::Leaf => write!(f, "."),
            Tree::Node(l, v, r) => write!(f, "({} {} {})", l, v, r),
        }
    }
}

fn main() {
    println!("{}", Color::Red);
    println!("{}", Point { x: 3.0, y: 4.0 });
    let p = Person { name: "Alice".into(), age: 30, email: "alice@ex.com".into() };
    println!("{}", p);
    println!("Debug: {:?}", p);

    let tree = Tree::Node(
        Box::new(Tree::Node(Box::new(Tree::Leaf), 1, Box::new(Tree::Leaf))),
        2,
        Box::new(Tree::Node(Box::new(Tree::Leaf), 3, Box::new(Tree::Leaf))),
    );
    println!("{}", tree);
}

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

    #[test]
    fn test_color_display() {
        assert_eq!(format!("{}", Color::Red), "Red");
        assert_eq!(format!("{}", Color::Green), "Green");
    }

    #[test]
    fn test_point_display() {
        assert_eq!(format!("{}", Point { x: 3.0, y: 4.0 }), "(3.0, 4.0)");
    }

    #[test]
    fn test_person_display() {
        let p = Person { name: "Alice".into(), age: 30, email: "alice@ex.com".into() };
        assert_eq!(format!("{}", p), "Alice (age 30, alice@ex.com)");
    }

    #[test]
    fn test_tree_display() {
        let tree = Tree::Node(
            Box::new(Tree::Node(Box::new(Tree::Leaf), 1, Box::new(Tree::Leaf))),
            2,
            Box::new(Tree::Node(Box::new(Tree::Leaf), 3, Box::new(Tree::Leaf))),
        );
        assert_eq!(format!("{}", tree), "((. 1 .) 2 (. 3 .))");
    }
}
(* 083: Display โ€” custom string representation *)

(* Approach 1: Simple to_string *)
type color = Red | Green | Blue

let color_to_string = function
  | Red -> "Red"
  | Green -> "Green"
  | Blue -> "Blue"

type point = { x: float; y: float }

let point_to_string p =
  Printf.sprintf "(%.1f, %.1f)" p.x p.y

(* Approach 2: Complex formatting *)
type person = { name: string; age: int; email: string }

let person_to_string p =
  Printf.sprintf "%s (age %d, %s)" p.name p.age p.email

let person_to_debug p =
  Printf.sprintf "{ name = %S; age = %d; email = %S }" p.name p.age p.email

(* Approach 3: Recursive display *)
type 'a tree = Leaf | Node of 'a tree * 'a * 'a tree

let rec tree_to_string to_s = function
  | Leaf -> "."
  | Node (l, v, r) ->
    Printf.sprintf "(%s %s %s)"
      (tree_to_string to_s l)
      (to_s v)
      (tree_to_string to_s r)

(* Tests *)
let () =
  assert (color_to_string Red = "Red");
  assert (point_to_string { x = 3.0; y = 4.0 } = "(3.0, 4.0)");
  let p = { name = "Alice"; age = 30; email = "alice@ex.com" } in
  assert (person_to_string p = "Alice (age 30, alice@ex.com)");
  let t = Node (Node (Leaf, 1, Leaf), 2, Node (Leaf, 3, Leaf)) in
  assert (tree_to_string string_of_int t = "((. 1 .) 2 (. 3 .))");
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Core Insight

`Display` controls how a type is printed with `{}`. Unlike `Debug` (derived), `Display` must be manually implemented โ€” it's the user-facing representation.

OCaml Approach

  • Write `to_string` function manually
  • Use `Printf.sprintf` with format strings
  • No unified "display" protocol

Rust Approach

  • `impl fmt::Display for Type`
  • Enables `format!("{}", x)`, `println!("{}", x)`
  • Single `fmt` method returns `fmt::Result`

Comparison Table

FeatureOCamlRust
ProtocolManual `to_string``impl Display`
Format string`%s` with `to_string``{}` automatic
Debug`#show` (ppx)`#[derive(Debug)]`
DeriveNoDisplay: no, Debug: yes