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