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

Example 071: Collatz Conjecture

Difficulty: โญ Category: Recursion Concept: Computing the Collatz (3n+1) sequence step count. Demonstrates simple recursion with guards, a Result-typed safe API, and the iterative equivalent. A classic exercise for pattern matching and error handling. OCaml โ†’ Rust insight: Both languages express the Collatz logic identically with pattern matching; the difference is OCaml uses `if/else` chains while Rust's `match` with guards (`n if n % 2 == 0`) is more idiomatic.
/// Collatz Conjecture
///
/// Computing the 3n+1 sequence step count. Demonstrates simple recursion,
/// Result-typed safe API, and iterative variants.

/// Naive recursive โ€” mirrors OCaml's version directly.
pub fn collatz_steps(n: u64) -> u64 {
    match n {
        1 => 0,
        n if n % 2 == 0 => 1 + collatz_steps(n / 2),
        n => 1 + collatz_steps(3 * n + 1),
    }
}

/// Safe API with Result โ€” rejects non-positive inputs.
pub fn collatz(n: i64) -> Result<u64, String> {
    if n <= 0 {
        Err("Only positive integers are allowed".to_string())
    } else {
        Ok(collatz_steps(n as u64))
    }
}

/// Iterative version โ€” idiomatic Rust, no recursion.
pub fn collatz_iter(n: i64) -> Result<u64, String> {
    if n <= 0 {
        return Err("Only positive integers are allowed".to_string());
    }
    let mut current = n as u64;
    let mut steps = 0u64;
    while current != 1 {
        current = if current % 2 == 0 {
            current / 2
        } else {
            3 * current + 1
        };
        steps += 1;
    }
    Ok(steps)
}

/// Generate the full Collatz sequence.
pub fn collatz_sequence(n: u64) -> Vec<u64> {
    let mut seq = vec![n];
    let mut current = n;
    while current != 1 {
        current = if current % 2 == 0 {
            current / 2
        } else {
            3 * current + 1
        };
        seq.push(current);
    }
    seq
}

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

    #[test]
    fn test_collatz_1() {
        assert_eq!(collatz(1), Ok(0));
    }

    #[test]
    fn test_collatz_6() {
        assert_eq!(collatz(6), Ok(8));
    }

    #[test]
    fn test_collatz_11() {
        assert_eq!(collatz(11), Ok(14));
    }

    #[test]
    fn test_collatz_27() {
        assert_eq!(collatz(27), Ok(111));
    }

    #[test]
    fn test_collatz_negative() {
        assert!(collatz(-1).is_err());
        assert!(collatz(0).is_err());
    }

    #[test]
    fn test_iter_matches_recursive() {
        for n in 1..=100 {
            assert_eq!(collatz(n), collatz_iter(n));
        }
    }

    #[test]
    fn test_sequence() {
        assert_eq!(collatz_sequence(6), vec![6, 3, 10, 5, 16, 8, 4, 2, 1]);
    }
}

fn main() {
    println!("{:?}", collatz(1), Ok(0));
    println!("{:?}", collatz(6), Ok(8));
    println!("{:?}", collatz(11), Ok(14));
}
let rec collatz_steps n =
  if n = 1 then 0
  else if n mod 2 = 0 then 1 + collatz_steps (n / 2)
  else 1 + collatz_steps (3 * n + 1)

let collatz n =
  if n <= 0 then Error "Only positive integers are allowed"
  else Ok (collatz_steps n)

let () =
  assert (collatz 1 = Ok 0);
  assert (collatz 6 = Ok 8);
  assert (collatz 27 = Ok 111);
  assert (collatz (-1) = Error "Only positive integers are allowed");
  print_endline "All assertions passed."

๐Ÿ“Š Detailed Comparison

Collatz Conjecture: OCaml vs Rust

The Core Insight

The Collatz sequence is simple enough that both languages express it almost identically. The interesting comparison is in error handling: OCaml's `Result` type and Rust's `Result<T, E>` serve the same purpose but with different ergonomics around the `?` operator and pattern matching.

OCaml Approach

OCaml uses `if/else` chains for the three cases (n=1, even, odd). The `Result` type (`Ok`/`Error`) wraps the safe API. Pattern matching on the result uses `match ... with Ok s -> ... | Error e -> ...`. The recursive version relies on OCaml's guaranteed tail-call optimization for the `else` branches (though this particular recursion isn't strictly tail-recursive due to `1 + ...`).

Rust Approach

Rust uses `match` with guards (`n if n % 2 == 0 => ...`) for cleaner pattern matching. The `Result<u64, String>` return type forces callers to handle errors. The iterative version with `while current != 1` is more idiomatic Rust and avoids any stack concerns. The `?` operator (not shown here) could propagate errors even more concisely in larger pipelines.

Side-by-Side

ConceptOCamlRust
Pattern match`if n = 1 then ... else if ...``match n { 1 => ..., n if ... => ... }`
Error type`(int, string) result``Result<u64, String>`
Safe API`Ok (collatz_steps n)``Ok(collatz_steps(n as u64))`
Integer types`int` (63-bit)`u64` (explicit unsigned)
IterationRecursion (idiomatic)`while` loop (idiomatic)

What Rust Learners Should Notice

  • Match guards (`n if n % 2 == 0`) are Rust's way to add conditions to match arms โ€” cleaner than nested `if/else`
  • `Result<u64, String>` is the Rust equivalent of OCaml's `(int, string) result` โ€” both force explicit error handling
  • Rust's explicit integer types (`u64` vs `i64`) make the domain constraint (positive integers) partially expressible in the type system
  • The iterative version is preferred in Rust โ€” it's clear, stack-safe, and often faster
  • `as u64` is an explicit cast โ€” Rust never silently converts between integer types

Further Reading

  • [The Rust Book โ€” Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)
  • [Exercism Collatz Conjecture](https://exercism.org/tracks/ocaml/exercises/collatz-conjecture)