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

132: Phantom Units of Measure

Difficulty: โญโญ Level: Intermediate Tag numeric values with their unit of measure so you can't accidentally add metres to feet or pass a duration where a distance is expected.

The Problem This Solves

The Mars Climate Orbiter was lost in 1999 because one module sent thruster data in pound-force-seconds while another expected newton-seconds. Both were just floating-point numbers. The compiler had no idea they were incompatible. Total cost: $327 million. This class of bug is embarrassingly common in everyday code too: passing a price in cents where a price in dollars is expected, mixing up pixels and points in a UI, confusing seconds and milliseconds in a deadline computation. All of these are just `f64` or `i64` at the type level. The names are only in variable names and doc comments โ€” the compiler can't check them. Phantom types give each quantity a unit tag. `Quantity<Meters>` and `Quantity<Feet>` are different types wrapping the same `f64`. You can add two `Quantity<Meters>` values together (same unit), but you cannot add `Quantity<Meters>` to `Quantity<Feet>` โ€” the compiler rejects it. Physics relationships like "distance / time = speed" are expressed as specific `impl` blocks that produce the right output unit.

The Intuition

A phantom type is a type parameter that exists in the type signature but isn't stored anywhere in memory. `struct Quantity<Unit> { value: f64, _unit: PhantomData<Unit> }` stores exactly one `f64`. The `Unit` parameter โ€” `Meters`, `Seconds`, `Kilograms` โ€” is just a label carried in the type. At runtime, a `Quantity<Meters>` and a `Quantity<Seconds>` are identical in memory. But to the compiler, they're completely different types. The unit-aware arithmetic is implemented as specific trait impls. You write `impl Add for Quantity<Meters>` (same-unit addition is fine), and `impl Div<Quantity<Seconds>> for Quantity<Meters>` to say "distance divided by time gives speed." The compiler checks units at every operation. If you try to add metres and seconds, there's no `impl Add<Quantity<Seconds>> for Quantity<Meters>` โ€” compile error.

How It Works in Rust

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

// Unit markers โ€” empty structs, just type labels
struct Meters;
struct Seconds;
struct Kilograms;
struct MetersPerSecond;  // a derived unit

// The core wrapper โ€” value stored, unit is just a phantom tag
#[derive(Debug, Clone, Copy)]
struct Quantity<Unit> {
 value: f64,
 _unit: PhantomData<Unit>,  // zero bytes โ€” pure type-level information
}

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

// Same-unit addition: only compiles when both operands have the same unit U
impl<U> Add for Quantity<U> {
 type Output = Quantity<U>;
 fn add(self, rhs: Self) -> Self::Output { Quantity::new(self.value + rhs.value) }
}

// Physics: metres รท seconds = metres/second
// This impl only exists for this specific combination of units
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)
 }
}

// Physics: metres/second ร— seconds = metres
impl Mul<Quantity<Seconds>> for Quantity<MetersPerSecond> {
 type Output = Quantity<Meters>;
 fn mul(self, rhs: Quantity<Seconds>) -> Self::Output {
     Quantity::new(self.value * rhs.value)
 }
}
Usage:
let d1: Quantity<Meters>  = Quantity::new(100.0);
let d2: Quantity<Meters>  = Quantity::new(50.0);
let total = d1 + d2;  // Quantity<Meters> โœ“

let t: Quantity<Seconds> = Quantity::new(15.0);
let speed = total / t;  // Quantity<MetersPerSecond> โ€” unit derived automatically

// let bad = d1 + t;  // compile error: no impl Add<Quantity<Seconds>> for Quantity<Meters>
Unit conversion:
trait ConvertTo<Target> { fn convert(self) -> Quantity<Target>; }

impl ConvertTo<Kilometers> for Quantity<Meters> {
 fn convert(self) -> Quantity<Kilometers> { Quantity::new(self.value / 1000.0) }
}

What This Unlocks

Key Differences

ConceptOCamlRust
Unit phantom`type 'unit quantity = { value: float }` โ€” parameter is phantom`struct Quantity<Unit> { value: f64, _unit: PhantomData<Unit> }`
Same-unit addType annotation `(a : 'u quantity) -> (b : 'u quantity) -> 'u quantity``impl<U> Add for Quantity<U>` โ€” any same-unit pair
Unit arithmeticExplicit type annotation at call siteSpecific `impl Div<Quantity<Seconds>> for Quantity<Meters>`
ConversionExplicit function with new phantomTrait `ConvertTo<Target>` โ€” explicit, type-safe
// Example 132: Units of Measure via Phantom Types
use std::marker::PhantomData;
use std::ops::{Add, Mul, Div};

// Approach 1: Phantom type units
struct Meters;
struct Seconds;
struct Kilograms;
struct MetersPerSecond;

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

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

    fn value(&self) -> f64 {
        self.value
    }
}

// Same-unit addition
impl<U> Add for Quantity<U> {
    type Output = Quantity<U>;
    fn add(self, rhs: Self) -> 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)
    }
}

// Distance / Time = Speed
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)
    }
}

// Speed * Time = Distance
impl Mul<Quantity<Seconds>> for Quantity<MetersPerSecond> {
    type Output = Quantity<Meters>;
    fn mul(self, rhs: Quantity<Seconds>) -> Self::Output {
        Quantity::new(self.value * rhs.value)
    }
}

// Approach 2: Generic unit display
trait UnitName {
    fn name() -> &'static str;
}

impl UnitName for Meters { fn name() -> &'static str { "m" } }
impl UnitName for Seconds { fn name() -> &'static str { "s" } }
impl UnitName for Kilograms { fn name() -> &'static str { "kg" } }
impl UnitName for MetersPerSecond { fn name() -> &'static str { "m/s" } }

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

// Approach 3: Conversion between units
struct Kilometers;
struct Miles;

impl UnitName for Kilometers { fn name() -> &'static str { "km" } }
impl UnitName for Miles { fn name() -> &'static str { "mi" } }

trait ConvertTo<Target> {
    fn convert(self) -> Quantity<Target>;
}

impl ConvertTo<Kilometers> for Quantity<Meters> {
    fn convert(self) -> Quantity<Kilometers> {
        Quantity::new(self.value / 1000.0)
    }
}

impl ConvertTo<Miles> for Quantity<Kilometers> {
    fn convert(self) -> Quantity<Miles> {
        Quantity::new(self.value * 0.621371)
    }
}

fn main() {
    let distance: Quantity<Meters> = Quantity::new(100.0);
    let distance2: Quantity<Meters> = Quantity::new(50.0);
    let total = distance + distance2;
    println!("Total: {}", total);

    let time: Quantity<Seconds> = Quantity::new(10.0);
    let speed = total / time;
    println!("Speed: {}", speed);

    let new_dist = speed * Quantity::<Seconds>::new(5.0);
    println!("New distance: {}", new_dist);

    let km: Quantity<Kilometers> = Quantity::<Meters>::new(5000.0).convert();
    let mi: Quantity<Miles> = km.convert();
    println!("{} = {}", km, mi);
}

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

    #[test]
    fn test_same_unit_add() {
        let a: Quantity<Meters> = Quantity::new(10.0);
        let b: Quantity<Meters> = Quantity::new(20.0);
        assert_eq!((a + b).value(), 30.0);
    }

    #[test]
    fn test_scalar_mul() {
        let a: Quantity<Meters> = Quantity::new(5.0);
        assert_eq!((a * 3.0).value(), 15.0);
    }

    #[test]
    fn test_speed_computation() {
        let d = Quantity::<Meters>::new(100.0);
        let t = Quantity::<Seconds>::new(10.0);
        let s = d / t;
        assert_eq!(s.value(), 10.0);
    }

    #[test]
    fn test_speed_times_time() {
        let s = Quantity::<MetersPerSecond>::new(5.0);
        let t = Quantity::<Seconds>::new(10.0);
        let d = s * t;
        assert_eq!(d.value(), 50.0);
    }

    #[test]
    fn test_conversion() {
        let m = Quantity::<Meters>::new(1000.0);
        let km: Quantity<Kilometers> = m.convert();
        assert_eq!(km.value(), 1.0);
    }
}
(* Example 132: Units of Measure via Phantom Types *)

(* Approach 1: Phantom type units *)
type meters
type seconds
type kilograms

type 'unit quantity = { value : float }

let meters v : meters quantity = { value = v }
let seconds v : seconds quantity = { value = v }
let kilograms v : kilograms quantity = { value = v }

let add (a : 'u quantity) (b : 'u quantity) : 'u quantity =
  { value = a.value +. b.value }

let scale (a : 'u quantity) (s : float) : 'u quantity =
  { value = a.value *. s }

let get_value (q : 'u quantity) = q.value

(* Approach 2: Module-based units *)
module type UNIT = sig
  type t
  val name : string
end

module Meters : UNIT = struct type t = meters let name = "m" end
module Seconds : UNIT = struct type t = seconds let name = "s" end

module Quantity (U : UNIT) = struct
  type t = float
  let create v = v
  let add a b = a +. b
  let to_string v = Printf.sprintf "%.2f %s" v U.name
end

module M = Quantity(Meters)
module S = Quantity(Seconds)

(* Approach 3: Speed = meters / seconds *)
type speed
let compute_speed (d : meters quantity) (t : seconds quantity) : speed quantity =
  { value = d.value /. t.value }

(* Tests *)
let () =
  let d1 = meters 100.0 in
  let d2 = meters 50.0 in
  let total = add d1 d2 in
  assert (get_value total = 150.0);

  let t = seconds 10.0 in
  let s = compute_speed total t in
  assert (get_value s = 15.0);

  let scaled = scale d1 2.0 in
  assert (get_value scaled = 200.0);

  (* Module-based *)
  let m1 = M.create 5.0 in
  let m2 = M.create 3.0 in
  assert (M.add m1 m2 = 8.0);

  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Comparison: Units of Measure via Phantom Types

OCaml

๐Ÿช Show OCaml equivalent
type 'unit quantity = { value : float }

let meters v : meters quantity = { value = v }
let add (a : 'u quantity) (b : 'u quantity) : 'u quantity =
{ value = a.value +. b.value }

let compute_speed (d : meters quantity) (t : seconds quantity) : speed quantity =
{ value = d.value /. t.value }

Rust

struct Quantity<Unit> { value: f64, _unit: PhantomData<Unit> }

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

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)
 }
}