Type-Level Safety

PhantomData prevents mixing different kinds of units at the type level.

use std::marker::PhantomData;

struct Meter;
struct Second;

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

type Distance = Quantity<Meter>;
type Time = Quantity<Second>;

fn speed(d: Distance, t: Time) -> f64 {
    d.value / t.value
}

fn main() {
    let distance: Distance = Distance {value: 10.0, _unit: PhantomData};
    let time: Time = Quantity {value: 5.0, _unit: PhantomData};
    
    // This code will not compiled. As we passing the datatype is different.
    // Comment below line, and uncomment line next line. Then code compile and run.
    let result = speed(time, distance);
    // let result = speed(distance, time);
    
    println!("Speed: {}", result);
}

Type-safety without using PhantomData

Use can also achieve type safety without using PhantomData also.


struct Distance(f64);
struct Second(f64);

fn speed(distance: Distance, time: Second) -> f64 {
    distance.0 / time.0
}

fn main() {
    let distance = Distance(10.0);
    let time = Second(5.0);
    
    // This code will not compiled. As we passing the datatype is different.
    // Comment below line, and uncomment line next line. Then code compile and run.
    let result = speed(time, distance);
    // let result = speed(distance, time);
    
    println!("Speed: {}", result);
}