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

739: Phantom Units of Measure

Difficulty: 4 Level: Expert `Quantity<Meters>` and `Quantity<Feet>` are the same `f64` at runtime but incompatible types at compile time โ€” adding them is a type error, converting is explicit, and the unit arithmetic is zero-cost.

The Problem This Solves

Unit confusion is a real category of engineering disaster. The Mars Climate Orbiter was lost in 1999 because one module used imperial units and another used metric โ€” a `f64` passed from one to the other with no type-level indication of what it measured. The values looked plausible; the crash was a complete surprise. In everyday code the stakes are lower but the bugs are common: a function that takes a duration in milliseconds called with a value in seconds, a distance in feet added to a distance in metres. The compiler sees two `f64` values and is happy. The test suite misses it. The customer notices. Phantom types solve this with zero runtime cost. `Quantity<Meters>` and `Quantity<Feet>` hold the same `f64` but are different types. The `Add` trait is only defined for matching units โ€” `Quantity<U> + Quantity<U>`. Different units don't add. Conversion functions are explicit and named. F# has built-in units of measure; Rust achieves the same guarantee with phantom types.

The Intuition

The unit marker (`Meters`, `Feet`, `Seconds`) is a zero-sized type used as a type parameter. At runtime, `Quantity<Meters>` is just an `f64`. At compile time, it's a distinct type from `Quantity<Feet>`. `impl Add for Quantity<U>` is generic over `U` โ€” adding two `Quantity<Meters>` values gives a `Quantity<Meters>`, and adding two `Quantity<Feet>` gives `Quantity<Feet>`. But there's no `impl Add<Quantity<Feet>> for Quantity<Meters>` โ€” that's the intentional gap. Trying to add them fails to find an `Add` implementation and the compiler tells you clearly. Derived quantities (velocity = distance / time) use specific `Div` implementations: `impl Div<Quantity<Seconds>> for Quantity<Meters>` produces `Quantity<MetersPerSecond>`. The type algebra mirrors dimensional analysis.

How It Works in Rust

use std::marker::PhantomData;
use std::ops::{Add, Sub, Mul, Div};

// Unit markers โ€” zero bytes each
pub struct Meters;
pub struct Feet;
pub struct Seconds;
pub struct MetersPerSecond;

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Quantity<Unit> {
 value: f64,
 _unit: PhantomData<Unit>,  // zero bytes; carries unit in the type
}

impl<U> Quantity<U> {
 pub fn new(v: f64) -> Self { Quantity { value: v, _unit: PhantomData } }
 pub fn value(self) -> f64 { self.value }
}

// Same-unit addition โ€” U matches U
impl<U> Add for Quantity<U> {
 type Output = Quantity<U>;
 fn add(self, rhs: Self) -> Self::Output { Quantity::new(self.value + rhs.value) }
}

// Dimensional analysis: Meters / Seconds = MetersPerSecond
impl Div<Quantity<Seconds>> for Quantity<Meters> {
 type Output = Quantity<MetersPerSecond>;
 fn div(self, rhs: Quantity<Seconds>) -> Self::Output {
     Quantity::new(self.value / rhs.value)
 }
}

// Convenience constructors
pub fn meters(v: f64)  -> Quantity<Meters>  { Quantity::new(v) }
pub fn feet(v: f64)    -> Quantity<Feet>    { Quantity::new(v) }
pub fn seconds(v: f64) -> Quantity<Seconds> { Quantity::new(v) }

// Explicit conversion โ€” never implicit
pub fn feet_to_meters(f: Quantity<Feet>) -> Quantity<Meters> {
 Quantity::new(f.value() * 0.3048)
}

// โ”€โ”€ Valid operations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
let a = meters(100.0) + meters(50.0);   // Quantity<Meters> โ€” fine
let speed = meters(1000.0) / seconds(10.0);  // Quantity<MetersPerSecond>

let f = feet(328.084);
let m = feet_to_meters(f);  // explicit conversion required

// โ”€โ”€ Compile errors โ€” unit mismatch โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// meters(1.0) + feet(1.0);    // ERROR: Add<Quantity<Feet>> not implemented for Quantity<Meters>
// meters(1.0) + seconds(1.0); // ERROR: same
// let wrong: Quantity<Feet> = meters(5.0);  // ERROR: mismatched types

// Size: just f64 โ€” zero overhead
assert_eq!(std::mem::size_of::<Quantity<Meters>>(), std::mem::size_of::<f64>());

What This Unlocks

Key Differences

ConceptOCamlRust
Units of measureF# has native UoM; OCaml needs phantom types similarly to Rust`Quantity<Unit>` with `PhantomData<Unit>` โ€” same idiom
Addition restriction`add_same` function with explicit unit type`impl<U> Add for Quantity<U>` โ€” only same-unit addition
Dimensional arithmeticManual phantom type propagation`impl Div<Quantity<Seconds>> for Quantity<Meters>` โ€” explicit type algebra
ConversionExplicit function`feet_to_meters(f: Quantity<Feet>) -> Quantity<Meters>` โ€” explicit, never implicit
Runtime costZero (phantom type erased)Zero โ€” `PhantomData` adds no bytes; compiles to bare `f64` ops
/// 739: Phantom Units of Measure โ€” Meters vs Feet vs Seconds
/// Unit mixing is a compile-time error. All checks have zero runtime cost.

use std::marker::PhantomData;
use std::ops::{Add, Mul, Div};

// โ”€โ”€ Unit markers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

pub struct Meters;
pub struct Feet;
pub struct Seconds;
pub struct MetersPerSecond;
pub struct FeetPerSecond;

// โ”€โ”€ Measurement โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Quantity<Unit> {
    value: f64,
    _unit: PhantomData<Unit>,
}

impl<U> Quantity<U> {
    pub fn new(v: f64) -> Self { Quantity { value: v, _unit: PhantomData } }
    pub fn value(self) -> f64 { self.value }
}

impl<U> std::fmt::Display for Quantity<U> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:.4}", self.value)
    }
}

// โ”€โ”€ Same-unit arithmetic โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

impl<U> Add for Quantity<U> {
    type Output = Quantity<U>;
    fn add(self, rhs: Self) -> Self::Output {
        Quantity::new(self.value + rhs.value)
    }
}

impl<U> std::ops::Sub for Quantity<U> {
    type Output = Quantity<U>;
    fn sub(self, rhs: Self) -> Self::Output {
        Quantity::new(self.value - rhs.value)
    }
}

// โ”€โ”€ Velocity: meters / seconds = m/s โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

impl Div<Quantity<Seconds>> for Quantity<Meters> {
    type Output = Quantity<MetersPerSecond>;
    fn div(self, rhs: Quantity<Seconds>) -> Self::Output {
        Quantity::new(self.value / rhs.value)
    }
}

// โ”€โ”€ Scalar multiplication โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

impl<U> Mul<f64> for Quantity<U> {
    type Output = Quantity<U>;
    fn mul(self, rhs: f64) -> Self::Output {
        Quantity::new(self.value * rhs)
    }
}

// โ”€โ”€ Unit conversions (explicit!) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

pub fn feet_to_meters(f: Quantity<Feet>) -> Quantity<Meters> {
    Quantity::new(f.value() * 0.3048)
}

pub fn meters_to_feet(m: Quantity<Meters>) -> Quantity<Feet> {
    Quantity::new(m.value() / 0.3048)
}

// โ”€โ”€ Convenience constructors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

pub fn meters(v: f64)  -> Quantity<Meters>  { Quantity::new(v) }
pub fn feet(v: f64)    -> Quantity<Feet>    { Quantity::new(v) }
pub fn seconds(v: f64) -> Quantity<Seconds> { Quantity::new(v) }

fn main() {
    let a = meters(100.0);
    let b = meters(50.0);
    let c = a + b;
    println!("100m + 50m = {}m", c);

    let f = feet(328.084);
    let m = feet_to_meters(f);
    println!("328.084 ft = {}m", m);

    let dist = meters(1000.0);
    let time = seconds(10.0);
    let speed = dist / time;
    println!("1000m / 10s = {} m/s", speed);

    // These DO NOT COMPILE:
    // let wrong = meters(1.0) + feet(1.0);  // ERROR: type mismatch
    // let wrong = meters(1.0) + seconds(1.0); // ERROR: type mismatch
    println!("\nAll unit checks passed at compile time โ€” zero runtime cost!");
}

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

    #[test]
    fn add_same_unit() {
        let a = meters(3.0);
        let b = meters(4.0);
        assert_eq!((a + b).value(), 7.0);
    }

    #[test]
    fn sub_same_unit() {
        let a = feet(10.0);
        let b = feet(3.0);
        assert_eq!((a - b).value(), 7.0);
    }

    #[test]
    fn feet_to_meters_conversion() {
        let f = feet(1.0);
        let m = feet_to_meters(f);
        let diff = (m.value() - 0.3048).abs();
        assert!(diff < 1e-10, "Expected 0.3048, got {}", m.value());
    }

    #[test]
    fn meters_to_feet_roundtrip() {
        let m = meters(1.0);
        let f = meters_to_feet(m);
        let back = feet_to_meters(f);
        let diff = (back.value() - 1.0).abs();
        assert!(diff < 1e-10);
    }

    #[test]
    fn velocity_from_distance_and_time() {
        let d = meters(100.0);
        let t = seconds(10.0);
        let v = d / t;
        assert_eq!(v.value(), 10.0);
    }

    #[test]
    fn scalar_multiply() {
        let m = meters(5.0) * 3.0;
        assert_eq!(m.value(), 15.0);
    }
}
(* 739: Phantom Units of Measure โ€” OCaml *)
(* OCaml doesn't have F#'s native units, but we can use phantom types *)

type meters  = Meters
type feet    = Feet
type seconds = Seconds

(* Phantom-typed measurement *)
type 'unit measure = { value: float }

let meters  v : meters measure  = { value = v }
let feet    v : feet measure    = { value = v }
let seconds v : seconds measure = { value = v }

(* Same-unit addition is safe *)
let add_same (a : 'u measure) (b : 'u measure) : 'u measure =
  { value = a.value +. b.value }

(* Explicit conversion โ€” no implicit mixing *)
let feet_to_meters (f : feet measure) : meters measure =
  { value = f.value *. 0.3048 }

let meters_to_feet (m : meters measure) : feet measure =
  { value = m.value /. 0.3048 }

let print_m label (m : meters measure)  = Printf.printf "%s = %.2f m\n" label m.value
let print_f label (f : feet measure)    = Printf.printf "%s = %.2f ft\n" label f.value
let print_s label (s : seconds measure) = Printf.printf "%s = %.2f s\n" label s.value

let () =
  let a = meters 10.0 in
  let b = meters 5.0 in
  let c = add_same a b in           (* OK: same phantom type *)
  print_m "10m + 5m" c;
  let d = feet 32.8084 in
  let e = feet_to_meters d in
  print_m "32.8084 ft โ†’ m" e;
  let t = seconds 60.0 in
  print_s "duration" t;
  (* add_same a t โ† type error: meters โ‰  seconds *)
  ignore (meters_to_feet c)