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

778: Fibonacci Computed at Compile Time

Difficulty: 3 Level: Intermediate A table of the first 93 Fibonacci numbers computed by the compiler, stored in the binary's read-only segment โ€” O(1) lookup at zero runtime cost.

The Problem This Solves

Fibonacci numbers appear in surprising places: Bloom filter sizing, hash table growth factors, fractal rendering, art and architecture, financial models. Computing them at runtime is straightforward, but it's wasteful when you always need the same precomputed values. `fib(80)` is always 23,416,728,348,161,557,660 โ€” computing it every time is pointless. The recursive implementation is instructive but exponentially slow: `fib(40)` requires ~1 billion recursive calls. The memoized iterative version is O(n) but allocates a `Vec`. For a table of fixed size (the first 93 values fit in `u64`), the right answer is to compute the entire table at compile time and embed it in the binary. This also demonstrates a subtle point: `const fn` cannot use `for` loops over iterators yet, but it can use `while` loops and direct array indexing. Knowing this constraint shapes how you write compile-time code.

The Intuition

This is the same idea as a sin/cos lookup table in embedded graphics, or a prime sieve embedded in a number theory library. You compute values that are mathematically fixed, store them as a `const` array, and at runtime just do an array lookup. `FIB_TABLE[n]` is a single memory read. Compared to even the memoized runtime version (which allocates a `Vec`), it's faster, uses less memory, and has no allocation cost. Compared to the recursive version, the speedup is astronomical.

How It Works in Rust

// const fn: iterative โ€” loops are required (recursive const fn depth is limited)
const fn fib_iter(n: u32) -> u64 {
 if n == 0 { return 0; }
 if n == 1 { return 1; }
 let mut a: u64 = 0;
 let mut b: u64 = 1;
 let mut i = 2;
 while i <= n {               // MUST be while, not for
     let c = a + b;
     a = b;
     b = c;
     i += 1;
 }
 b
}

// Build the entire table at compile time using a const block
// fib(93) = 12,200,160,415,121,876,738 โ€” the last value that fits in u64
const FIB_TABLE: [u64; 93] = {
 let mut t = [0u64; 93];
 t[0] = 0;
 t[1] = 1;
 let mut i = 2usize;
 while i < 93 {               // while loop โ€” no iterators in const yet
     t[i] = t[i-1] + t[i-2]; // same recurrence, but evaluated by the compiler
     i += 1;
 }
 t                            // the array becomes the value of the const expression
};

// O(1) lookup โ€” the compiler already did the work
pub fn fib(n: usize) -> Option<u64> {
 FIB_TABLE.get(n).copied()    // None if n >= 93 (would overflow u64)
}

// For comparison โ€” runtime versions you should NOT use for fixed lookups:
pub fn fib_recursive(n: u32) -> u64 {
 match n { 0 => 0, 1 => 1, n => fib_recursive(n-1) + fib_recursive(n-2) }
 // fib_recursive(40) โ‰ˆ 1 billion calls โ€” don't do this
}

// When you need truly large Fibonacci numbers (n > 92), use matrix exponentiation
// F(n) via 2ร—2 matrix: O(log n), works for arbitrary precision types
fn fib_matrix(n: u64) -> u64 { ... }  // see example.rs for full implementation
The const block `{ let mut t = ...; while ...; t }` is a const block expression โ€” the entire block is evaluated at compile time, and the final expression `t` is the value. This is how you build complex constants that can't be expressed as a single formula.
// Verification โ€” the compiler checks this at compile time:
const _: () = assert!(FIB_TABLE[10] == 55);    // 0,1,1,2,3,5,8,13,21,34,55
const _: () = assert!(FIB_TABLE[20] == 6765);
// If these fail, the BUILD fails โ€” not a runtime error
Key points:

What This Unlocks

Key Differences

ConceptOCamlRust
Compile-time tableModule-level `let` (run at startup)`const FIB_TABLE: [u64; 93] = { ... }`
Const iterationN/A`while` loop with index โ€” `for` not const-stable
Table in binaryStartup-initialized `Array.t``const` array in `.rodata` โ€” zero runtime cost
Overflow protection`Int64` arithmeticExplicit `u64` โ€” `fib(93)` overflows, hence `[u64; 93]`
Bounds safety`Array.get` raises `Invalid_argument``.get(n).copied()` returns `Option<u64>`
Very large FibonacciZarith arbitrary precision`num-bigint` crate or matrix exponentiation
// 778. Fibonacci Computed at Compile Time
// const fn table: embedded in binary, O(1) lookup

// โ”€โ”€ const fn: iterative (required in const context) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

const fn fib_iter(n: u32) -> u64 {
    if n == 0 { return 0; }
    if n == 1 { return 1; }
    let mut a: u64 = 0;
    let mut b: u64 = 1;
    let mut i = 2;
    while i <= n {
        let c = a + b;
        a = b;
        b = c;
        i += 1;
    }
    b
}

// โ”€โ”€ Compile-time table of first 93 Fibonacci numbers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

// fib(93) is the last one that fits in u64
const FIB_TABLE: [u64; 93] = {
    let mut t = [0u64; 93];
    t[0] = 0;
    t[1] = 1;
    let mut i = 2usize;
    while i < 93 {
        t[i] = t[i-1] + t[i-2];
        i += 1;
    }
    t
};

/// O(1) Fibonacci lookup โ€” table is in the binary's read-only segment
pub fn fib(n: usize) -> Option<u64> {
    FIB_TABLE.get(n).copied()
}

// โ”€โ”€ Runtime recursive (for comparison) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

pub fn fib_recursive(n: u32) -> u64 {
    match n { 0 => 0, 1 => 1, n => fib_recursive(n-1) + fib_recursive(n-2) }
}

// โ”€โ”€ Memoized runtime (for comparison) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

pub fn fib_memoized(n: usize) -> u64 {
    let mut memo = vec![0u64; n + 1];
    if n >= 1 { memo[1] = 1; }
    for i in 2..=n {
        memo[i] = memo[i-1] + memo[i-2];
    }
    memo[n]
}

// โ”€โ”€ Matrix exponentiation (for very large n) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

/// F(n) via matrix exponentiation โ€” O(log n), works for any n
pub fn fib_matrix(n: u64) -> u128 {
    if n == 0 { return 0; }
    fn mat_mul(a: [[u128; 2]; 2], b: [[u128; 2]; 2]) -> [[u128; 2]; 2] {
        [[a[0][0]*b[0][0] + a[0][1]*b[1][0], a[0][0]*b[0][1] + a[0][1]*b[1][1]],
         [a[1][0]*b[0][0] + a[1][1]*b[1][0], a[1][0]*b[0][1] + a[1][1]*b[1][1]]]
    }
    fn mat_pow(mut m: [[u128; 2]; 2], mut p: u64) -> [[u128; 2]; 2] {
        let mut result = [[1, 0], [0, 1]]; // identity
        while p > 0 {
            if p & 1 == 1 { result = mat_mul(result, m); }
            m = mat_mul(m, m);
            p >>= 1;
        }
        result
    }
    let m = mat_pow([[1, 1], [1, 0]], n);
    m[0][1]
}

fn main() {
    println!("Compile-time Fibonacci table (first 20):");
    for i in 0..20 {
        println!("  fib({i:2}) = {}", FIB_TABLE[i]);
    }

    println!("\nLookup via fib():");
    println!("  fib(50) = {:?}", fib(50));
    println!("  fib(92) = {:?}", fib(92)); // largest u64 fib
    println!("  fib(93) = {:?}", fib(93)); // None โ€” out of range

    println!("\nMatrix exponentiation (arbitrary size, u128):");
    println!("  fib(100) = {}", fib_matrix(100));
}

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

    #[test]
    fn table_starts_correct() {
        assert_eq!(FIB_TABLE[0], 0);
        assert_eq!(FIB_TABLE[1], 1);
        assert_eq!(FIB_TABLE[2], 1);
        assert_eq!(FIB_TABLE[3], 2);
        assert_eq!(FIB_TABLE[10], 55);
    }

    #[test]
    fn fib_iter_matches_table() {
        for i in 0..20u32 {
            assert_eq!(fib_iter(i), FIB_TABLE[i as usize], "fib({i})");
        }
    }

    #[test]
    fn recursive_matches_table() {
        for i in 0..15u32 {
            assert_eq!(fib_recursive(i), FIB_TABLE[i as usize]);
        }
    }

    #[test]
    fn out_of_range_returns_none() {
        assert_eq!(fib(93), None);
    }

    #[test]
    fn matrix_fib_correct() {
        for i in 0u64..20 {
            assert_eq!(fib_matrix(i), FIB_TABLE[i as usize] as u128, "matrix fib({i})");
        }
    }
}
(* Fibonacci at 'compile time' (load time) in OCaml *)

(* Elegant recursive definition *)
let rec fib_rec n =
  if n <= 1 then n
  else fib_rec (n-1) + fib_rec (n-2)

(* Memoized version (Hashtbl) *)
let memo = Hashtbl.create 100

let rec fib_memo n =
  if n <= 1 then n
  else match Hashtbl.find_opt memo n with
  | Some v -> v
  | None ->
    let v = fib_memo (n-1) + fib_memo (n-2) in
    Hashtbl.replace memo n v;
    v

(* Table (computed once at module load โ€” analogous to const in Rust) *)
let fib_table =
  let t = Array.make 93 0 in
  t.(0) <- 0; t.(1) <- 1;
  for i = 2 to 92 do
    t.(i) <- t.(i-1) + t.(i-2)
  done;
  t

let fib n = fib_table.(n)

let () =
  Printf.printf "Recursive  fib(10) = %d\n" (fib_rec 10);
  Printf.printf "Memoized   fib(40) = %d\n" (fib_memo 40);
  Printf.printf "Table      fib(50) = %d\n" (fib 50);
  Printf.printf "fib(1..10) = ";
  for i = 0 to 10 do
    Printf.printf "%d " (fib i)
  done;
  print_newline ()