๐Ÿฆ€ Functional Rust
๐ŸŽฌ Rust Ownership in 30 seconds Visual walkthrough of ownership, moves, and automatic memory management.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Each value in Rust has exactly one owner โ€” when the owner goes out of scope, the value is dropped

โ€ข Assignment moves ownership by default; the original binding becomes invalid

โ€ข Borrowing (&T / &mut T) lets you reference data without taking ownership

โ€ข The compiler enforces: many shared references OR one mutable reference, never both

โ€ข No garbage collector needed โ€” memory is freed deterministically at scope exit

101: Lazy Sequences

Difficulty: 2 Level: Intermediate Work with infinite sequences โ€” Fibonacci numbers, primes, naturals โ€” without computing all of them upfront.

The Problem This Solves

Some problems are naturally expressed as infinite sequences: all prime numbers, all Fibonacci numbers, an infinite stream of events. You only ever need a finite prefix, but you want to describe the sequence as if it were infinite and let the consumer decide how much to take. Eager evaluation fails here โ€” you can't put infinitely many Fibonacci numbers in a `Vec`. You need a structure that computes values on demand, only when consumed. This is the distinction between "compute now" and "compute when needed" โ€” the latter is called lazy evaluation.

The Intuition

Rust's iterators are lazy by default. Writing `(0..)` doesn't allocate an infinite list โ€” it creates a description of how to produce natural numbers. The numbers are only computed when you call `.next()` on the iterator, which happens inside `.take(10).collect()`. This is different from OCaml, where you need the explicit `Seq` module to opt into laziness. In Rust, every iterator is lazy. The pipeline `naturals().filter(is_prime).take(10).collect()` runs the filter and take in a single pass โ€” no intermediate lists.

How It Works in Rust

// Infinite natural numbers โ€” just a range
pub fn naturals(start: u64) -> impl Iterator<Item = u64> {
 start..   // lazy โ€” no values computed yet
}

// Fibonacci via successors โ€” each step produces (current, next_pair)
pub fn fibs() -> impl Iterator<Item = u64> {
 std::iter::successors(Some((0u64, 1u64)), |&(a, b)| {
     a.checked_add(b).map(|s| (b, s))  // stop if overflow
 })
 .map(|(a, _)| a)  // extract just the current value
}

// Primes via filter โ€” lazy sieve (simple, not the most efficient)
pub fn primes() -> impl Iterator<Item = u64> {
 (2..).filter(|&n| is_prime(n))  // only compute is_prime when needed
}
Using them:
let first_10_fibs: Vec<u64> = fibs().take(10).collect();
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

// Primes between 100 and 200
let primes_100_200: Vec<u64> = primes()
 .skip_while(|&p| p < 100)
 .take_while(|&p| p <= 200)
 .collect();
You can also write generic unfold using `from_fn`:
pub fn unfold<S, T>(seed: S, f: impl Fn(&S) -> Option<(T, S)>) -> impl Iterator<Item = T> {
 let mut state = Some(seed);
 std::iter::from_fn(move || {
     let s = state.take()?;
     let (value, next) = f(&s)?;
     state = Some(next);
     Some(value)
 })
}

What This Unlocks

Key Differences

ConceptOCamlRust
Lazy by defaultNo โ€” use `Seq` module explicitlyYes โ€” all iterators are lazy
Infinite range`Seq.ints 0` (since 4.07)`0..` built-in range
Unfold`Seq.unfold``std::iter::from_fn` or `successors`
Consuming`Seq.take n seq> List.of_seq``.take(n).collect::<Vec<_>>()`
OverheadClosure allocation per stepZero-cost abstractions (monomorphized)
//! # Lazy Sequences
//!
//! OCaml's `Seq` module provides lazy sequences. Rust's iterators are
//! lazy by default โ€” they only compute values when consumed.

// ---------------------------------------------------------------------------
// Approach A: Iterator adaptors (idiomatic Rust)
// ---------------------------------------------------------------------------

/// Infinite natural numbers starting from n
pub fn naturals(start: u64) -> impl Iterator<Item = u64> {
    start..
}

/// Fibonacci sequence as an iterator
pub fn fibs() -> impl Iterator<Item = u64> {
    std::iter::successors(Some((0u64, 1u64)), |&(a, b)| a.checked_add(b).map(|s| (b, s)))
        .map(|(a, _)| a)
}

/// Infinite prime number iterator
pub fn primes() -> impl Iterator<Item = u64> {
    (2..).filter(|&n| is_prime(n))
}

fn is_prime(n: u64) -> bool {
    if n < 2 { return false; }
    (2..).take_while(|&i| i * i <= n).all(|i| n % i != 0)
}

// ---------------------------------------------------------------------------
// Approach B: Custom unfold (mirrors OCaml's Seq.unfold)
// ---------------------------------------------------------------------------

pub fn unfold<S, T>(seed: S, f: impl Fn(&S) -> Option<(T, S)>) -> impl Iterator<Item = T> {
    let mut state = Some(seed);
    std::iter::from_fn(move || {
        let s = state.take()?;
        let (value, next) = f(&s)?;
        state = Some(next);
        Some(value)
    })
}

pub fn fibs_unfold() -> impl Iterator<Item = u64> {
    unfold((0u64, 1u64), |&(a, b)| Some((a, (b, a + b))))
}

// ---------------------------------------------------------------------------
// Approach C: Successors (std::iter::successors)
// ---------------------------------------------------------------------------

pub fn naturals_succ(start: u64) -> impl Iterator<Item = u64> {
    std::iter::successors(Some(start), |&n| Some(n + 1))
}

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

    #[test]
    fn test_naturals() {
        let first5: Vec<u64> = naturals(0).take(5).collect();
        assert_eq!(first5, vec![0, 1, 2, 3, 4]);
    }

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

    #[test]
    fn test_primes() {
        let first10: Vec<u64> = primes().take(10).collect();
        assert_eq!(first10, vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
    }

    #[test]
    fn test_unfold_fibs() {
        let first10: Vec<u64> = fibs_unfold().take(10).collect();
        assert_eq!(first10, vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]);
    }

    #[test]
    fn test_laziness() {
        // This doesn't hang because iterators are lazy
        let _infinite = naturals(0);
        let first = naturals(0).take(1).collect::<Vec<_>>();
        assert_eq!(first, vec![0]);
    }
}

fn main() {
    println!("{:?}", first5, vec![0, 1, 2, 3, 4]);
    println!("{:?}", first10, vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]);
    println!("{:?}", first10, vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
}
let naturals = Seq.ints 0

let fibs =
  Seq.unfold (fun (a, b) -> Some (a, (b, a + b))) (0, 1)

let primes =
  let is_prime n =
    n >= 2 && Seq.ints 2
    |> Seq.take_while (fun i -> i * i <= n)
    |> Seq.for_all (fun i -> n mod i <> 0)
  in
  Seq.ints 2 |> Seq.filter is_prime

let () =
  Printf.printf "Naturals: ";
  Seq.take 5 naturals |> Seq.iter (Printf.printf "%d ");
  Printf.printf "\nFibs: ";
  Seq.take 10 fibs |> Seq.iter (Printf.printf "%d ");
  Printf.printf "\nPrimes: ";
  Seq.take 10 primes |> Seq.iter (Printf.printf "%d ");
  print_newline ()

๐Ÿ“Š Detailed Comparison

Comparison: Lazy Sequences โ€” OCaml vs Rust

Core Insight

OCaml's `Seq` provides lazy sequences as an explicit abstraction layered on top of eager lists. Rust's iterators are lazy from the ground up โ€” `map`, `filter`, `take` all return lazy adaptors that only evaluate when consumed by `collect`, `for_each`, etc. This design means Rust doesn't need a separate `Seq` module; every iterator is already a lazy sequence.

OCaml

๐Ÿช Show OCaml equivalent
let fibs = Seq.unfold (fun (a, b) -> Some (a, (b, a + b))) (0, 1)
let primes = Seq.ints 2 |> Seq.filter is_prime
let first10 = Seq.take 10 fibs |> Seq.iter (Printf.printf "%d ")

Rust

pub fn fibs() -> impl Iterator<Item = u64> {
 std::iter::successors(Some((0, 1)), |&(a, b)| Some((b, a + b)))
     .map(|(a, _)| a)
}

let first10: Vec<u64> = fibs().take(10).collect();

Comparison Table

AspectOCamlRust
Lazy by defaultNo (lists are eager)Yes (iterators are lazy)
Infinite range`Seq.ints 0``0..` (built-in)
Unfold`Seq.unfold f seed``std::iter::successors` or `from_fn`
Take N`Seq.take n seq``.take(n)`
Filter`Seq.filter pred seq``.filter(pred)`
Consume`Seq.iter f seq``.collect()` or `.for_each()`
Custom iteratorImplement `unit -> 'a Seq.node``impl Iterator for T`

Learner Notes

  • `impl Iterator<Item = T>`: Return type hides the concrete iterator type โ€” like OCaml's abstract `'a Seq.t`
  • `successors`: Generates `Some(next)` from previous โ€” perfect for Fibonacci-style sequences
  • `from_fn`: Most flexible โ€” closure with mutable state, returns `Option<T>` per call
  • No allocation: Rust's lazy chains allocate nothing until `collect()` โ€” OCaml's `Seq` also avoids allocation via thunks
  • `0..`: Rust's range syntax creates an infinite iterator โ€” no `Seq.ints` needed