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

086: Custom Iterator with State

Difficulty: Intermediate Category: Iterators Concept: Iterator with internal mutable state: counter, fibonacci Key Insight: Rust iterators are state machines โ€” `next()` mutates internal state and returns the next value.
// 086: Custom Iterator with State

// Approach 1: Counter iterator
struct Counter {
    current: i32,
    step: i32,
}

impl Counter {
    fn new(start: i32, step: i32) -> Self {
        Counter { current: start, step }
    }
}

impl Iterator for Counter {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        self.current += self.step;
        Some(self.current) // infinite
    }
}

// Approach 2: Fibonacci iterator
struct Fib { a: u64, b: u64 }

impl Fib {
    fn new() -> Self { Fib { a: 0, b: 1 } }
}

impl Iterator for Fib {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        let val = self.a;
        let next = self.a + self.b;
        self.a = self.b;
        self.b = next;
        Some(val)
    }
}

// Approach 3: Collatz sequence (finite)
struct Collatz { n: u64, done_: bool }

impl Collatz {
    fn new(start: u64) -> Self { Collatz { n: start, done_: false } }
}

impl Iterator for Collatz {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        if self.done_ { return None; }
        let val = self.n;
        if self.n == 1 {
            self.done_ = true;
        } else if self.n % 2 == 0 {
            self.n /= 2;
        } else {
            self.n = 3 * self.n + 1;
        }
        Some(val)
    }
}


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

    #[test]
    fn test_counter() {
        let v: Vec<i32> = Counter::new(0, 2).take(3).collect();
        assert_eq!(v, vec![2, 4, 6]);
    }

    #[test]
    fn test_counter_negative() {
        let v: Vec<i32> = Counter::new(10, -3).take(4).collect();
        assert_eq!(v, vec![7, 4, 1, -2]);
    }

    #[test]
    fn test_fibonacci() {
        let fibs: Vec<u64> = Fib::new().take(8).collect();
        assert_eq!(fibs, vec![0, 1, 1, 2, 3, 5, 8, 13]);
    }

    #[test]
    fn test_collatz() {
        let v: Vec<u64> = Collatz::new(6).collect();
        assert_eq!(v, vec![6, 3, 10, 5, 16, 8, 4, 2, 1]);
    }

    #[test]
    fn test_collatz_one() {
        let v: Vec<u64> = Collatz::new(1).collect();
        assert_eq!(v, vec![1]);
    }
}
(* 086: Custom Iterator with State *)

(* Approach 1: Counter using mutable ref *)
let make_counter start step =
  let n = ref (start - step) in
  fun () -> n := !n + step; Some !n

(* Approach 2: Fibonacci via Seq *)
let fibonacci () =
  let rec aux a b () = Seq.Cons (a, aux b (a + b)) in
  aux 0 1

let take_seq n s =
  let rec aux n s acc =
    if n <= 0 then List.rev acc
    else match s () with
      | Seq.Nil -> List.rev acc
      | Seq.Cons (x, rest) -> aux (n - 1) rest (x :: acc)
  in
  aux n s []

(* Approach 3: Collatz sequence iterator *)
let collatz_seq start =
  let rec aux n () =
    if n = 1 then Seq.Cons (1, fun () -> Seq.Nil)
    else Seq.Cons (n, aux (if n mod 2 = 0 then n / 2 else 3 * n + 1))
  in
  aux start

(* Tests *)
let () =
  let counter = make_counter 0 2 in
  assert (counter () = Some 2);
  assert (counter () = Some 4);
  assert (counter () = Some 6);
  let fibs = take_seq 8 (fibonacci ()) in
  assert (fibs = [0; 1; 1; 2; 3; 5; 8; 13]);
  let collatz = List.of_seq (collatz_seq 6) in
  assert (collatz = [6; 3; 10; 5; 16; 8; 4; 2; 1]);
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Core Insight

Custom iterators encapsulate state. Each call to `next()` advances the internal state machine. This is more explicit than OCaml's closure-based sequences.

OCaml Approach

  • Closure captures mutable ref: `let r = ref 0 in fun () -> incr r; !r`
  • Or pure: state threaded through `Seq` thunks

Rust Approach

  • Struct with state fields
  • `impl Iterator` with `next(&mut self)`
  • State mutation is explicit and checked by borrow rules

Comparison Table

FeatureOCamlRust
StateClosure ref / pure threadingStruct fields
Mutation`ref` / `incr``&mut self`
InfiniteLazy SeqIterator (never returns None)