🦀 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: Space Age — Float Computation

Difficulty: Beginner Category: Records and Variants Concept: Computation with named constants and pattern matching on variants Key Insight: Enum variants with pattern matching map directly between OCaml and Rust. Both use exhaustive matching to ensure all planets are handled.
/// Space Age — Float Computation with Variants
///
/// Ownership: Planet is Copy (enum with no data). All computations use f64 (Copy).

#[derive(Debug, Clone, Copy)]
pub enum Planet {
    Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune,
}

impl Planet {
    pub fn orbital_period(self) -> f64 {
        match self {
            Planet::Mercury => 0.2408467,
            Planet::Venus => 0.61519726,
            Planet::Earth => 1.0,
            Planet::Mars => 1.8808158,
            Planet::Jupiter => 11.862615,
            Planet::Saturn => 29.447498,
            Planet::Uranus => 84.016846,
            Planet::Neptune => 164.79132,
        }
    }

    pub const ALL: [Planet; 8] = [
        Planet::Mercury, Planet::Venus, Planet::Earth, Planet::Mars,
        Planet::Jupiter, Planet::Saturn, Planet::Uranus, Planet::Neptune,
    ];
}

const EARTH_YEAR_SECONDS: f64 = 31_557_600.0;

pub fn age_on(planet: Planet, seconds: f64) -> f64 {
    seconds / (EARTH_YEAR_SECONDS * planet.orbital_period())
}

/// Version 2: Using a lookup table instead of match
pub fn age_on_table(planet_index: usize, seconds: f64) -> f64 {
    const PERIODS: [f64; 8] = [
        0.2408467, 0.61519726, 1.0, 1.8808158,
        11.862615, 29.447498, 84.016846, 164.79132,
    ];
    seconds / (EARTH_YEAR_SECONDS * PERIODS[planet_index])
}

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

    fn approx(a: f64, b: f64) -> bool { (a - b).abs() < 0.01 }

    #[test]
    fn test_earth() {
        assert!(approx(age_on(Planet::Earth, 1_000_000_000.0), 31.69));
    }

    #[test]
    fn test_mercury() {
        assert!(approx(age_on(Planet::Mercury, 1_000_000_000.0), 131.56));
    }

    #[test]
    fn test_neptune() {
        assert!(approx(age_on(Planet::Neptune, 1_000_000_000.0), 0.19));
    }

    #[test]
    fn test_all_planets() {
        for &p in &Planet::ALL {
            let age = age_on(p, EARTH_YEAR_SECONDS);
            assert!(approx(age, 1.0 / p.orbital_period()));
        }
    }

    #[test]
    fn test_table_version() {
        assert!(approx(age_on_table(0, 1_000_000_000.0), 131.56));
    }
}

fn main() {
    println!("{:?}", approx(age_on(Planet::Earth, 1_000_000_000.0), 31.69));
    println!("{:?}", approx(age_on(Planet::Mercury, 1_000_000_000.0), 131.56));
    println!("{:?}", approx(age_on(Planet::Neptune, 1_000_000_000.0), 0.19));
}
(* Space Age — Float Computation *)

type planet = Mercury | Venus | Earth | Mars | Jupiter | Saturn | Uranus | Neptune

let orbital_period = function
  | Mercury -> 0.2408467 | Venus -> 0.61519726 | Earth -> 1.0
  | Mars -> 1.8808158 | Jupiter -> 11.862615 | Saturn -> 29.447498
  | Uranus -> 84.016846 | Neptune -> 164.79132

let earth_year_seconds = 31557600.0

let age_on planet seconds =
  seconds /. (earth_year_seconds *. orbital_period planet)

let () =
  let seconds = 1_000_000_000.0 in
  let age = age_on Earth seconds in
  assert (abs_float (age -. 31.69) < 0.01)

📊 Detailed Comparison

Space Age — Comparison

Core Insight

Simple enum + pattern matching translates almost identically between OCaml and Rust. When all data is Copy (enums, floats), ownership is invisible. The languages converge on the same clean pattern.

OCaml Approach

  • `type planet = Mercury | Venus | ...` — simple variant type
  • `let orbital_period = function | Mercury -> 0.24...` — pattern match function
  • `/.` for float division (separate operator from integer `/`)
  • No method syntax — free functions

Rust Approach

  • `enum Planet { Mercury, Venus, ... }` with `#[derive(Copy, Clone)]`
  • `impl Planet { fn orbital_period(self) -> f64 { match self { ... } } }`
  • `/` for both int and float division
  • `const ALL` array for iteration over all variants

Comparison Table

AspectOCamlRust
Variant`type planet = Mercury \...``enum Planet { Mercury, ... }`
Match`function \pat -> ...``match self { pat => ... }`
Float ops`/.` `.``/` ``
Constants`let x = 31557600.0``const X: f64 = 31_557_600.0`
Iterate variantsManual list`const ALL` array

Learner Notes

  • Rust has no separate float operators — type inference handles it
  • `const` in Rust is compile-time; OCaml `let` at module level is similar
  • Rust numeric literals support `_` separators: `31_557_600.0`
  • Both languages guarantee exhaustive matching — add a planet, compiler tells you