ExamplesBy LevelBy TopicLearning Paths
739 Advanced

739-phantom-units-of-measure — Phantom Units of Measure

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "739-phantom-units-of-measure — Phantom Units of Measure" functional Rust example. Difficulty level: Advanced. Key concepts covered: Functional Programming. The Mars Climate Orbiter was lost in 1999 because one module computed thrust in pound-force seconds and another expected newton-seconds. Key difference from OCaml: 1. **Language support**: F# has first

Tutorial

The Problem

The Mars Climate Orbiter was lost in 1999 because one module computed thrust in pound-force seconds and another expected newton-seconds. Unit confusion errors cost lives and billions of dollars. F# pioneered Units of Measure as a language feature; Rust and OCaml achieve the same protection via phantom types. A Length<Meters> cannot be added to Length<Feet> without an explicit conversion, catching unit errors at compile time with zero runtime overhead.

🎯 Learning Outcomes

  • • Represent physical units as zero-sized phantom marker types (Meters, Feet, Kilograms)
  • • Implement arithmetic that preserves or derives units: Length<M> + Length<M> -> Length<M>
  • • Prevent mixing incompatible units at compile time (Length<Meters> + Length<Feet> fails)
  • • Implement explicit unit conversion functions that change the phantom parameter
  • • See how the pattern extends to derived units (velocity = length / time)
  • Code Example

    #![allow(clippy::all)]
    //! # Phantom Units Of Measure
    
    pub fn placeholder() -> &'static str {
        "phantom-units-of-measure implementation"
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_placeholder() {
            assert!(!placeholder().is_empty());
        }
    }

    Key Differences

  • Language support: F# has first-class unit support; Rust and OCaml both use phantom types as a library-level approximation.
  • Derived units: Rust can encode Meters * Meters = MetersSquared via trait implementations; OCaml requires more verbose GADT indices to express multiplication of units.
  • Conversion safety: Both languages require explicit conversion functions; neither can infer unit equivalences automatically.
  • Ecosystem: Rust has uom (units of measurement) and dimensioned crates with full SI unit coverage; OCaml has no equivalent widely-used crate.
  • OCaml Approach

    F# has native units-of-measure syntax ([<Measure>] type m and float<m>). OCaml lacks this but achieves it via phantom types: type 'u length = Length of float. Jane Street's Validated and units-sexp libraries provide similar functionality. The OCaml community often uses Gg library for 3D geometry with typed vectors. GADTs allow encoding unit arithmetic relationships directly.

    Full Source

    #![allow(clippy::all)]
    //! # Phantom Units Of Measure
    
    pub fn placeholder() -> &'static str {
        "phantom-units-of-measure implementation"
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_placeholder() {
            assert!(!placeholder().is_empty());
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_placeholder() {
            assert!(!placeholder().is_empty());
        }
    }

    Deep Comparison

    Units of Measure

    See example files for comparison.

    Exercises

  • Add Time<Seconds> and implement Velocity<MetersPerSecond> as the result of dividing Length<Meters> by Time<Seconds>.
  • Implement a Celsius to Fahrenheit conversion that changes the phantom type: fn to_fahrenheit(t: Temperature<Celsius>) -> Temperature<Fahrenheit>.
  • Create a force function that multiplies Mass<Kilograms> by Acceleration<MetersPerSecondSquared> and returns Force<Newtons>.
  • Open Source Repos