Rust by Example

14.2.1 Unit conversions

A useful method of unit conversions:

use std::ops::Add; use std::marker::PhantomData; /// Null enumerations to define unit types #[derive(Debug, Copy)] struct Inch; #[derive(Debug, Copy)] struct Mm; /// Length is phantom type with hidden parameter `Unit` #[derive(Debug, Copy)] struct Length<Unit, T>(T,PhantomData<Unit>); /// impl X for Y {} means "implement the trait `X` for the Type `Y`" /// The following lines implement the `Add` trait for Length. /// /// The `<Unit, T: Add<T, Output=T>` after `impl` declares two generic /// types, `Unit`, which can be any type, and `T`, which is a type that /// must implement both traits `Copy` (which means no need to borrow, /// move, or clone; you can just pass in a variable, and both the caller /// and callee will own their own copy), and the trait `Add<T, Output=T>`. /// /// `Add<T, Output=T>` means that the type implements Add, taking in a T /// (meaning an i32 plus an i32, or an f64 plus an f64, etc.), and giving /// back a T (i32 + i32 = i32). /// /// So, this impl implements `Add<Length<Unit, T>` for `Length<Unit, T>`, /// which means you can add a `Length` to another `Length` of the same type. /// /// `type Output = Length<Unit, T>` means that this impl gives back a /// `Length<Unit, T>`, so that /// `Length<Mm, f64> + Length<Mm, f64> = Length<Mm, f64>` impl<Unit, T: Add<T, Output=T> + Copy> Add<Length<Unit, T>> for Length<Unit, T> { type Output = Length<Unit, T>; fn add(self, r: Length<Unit, T>) -> Length<Unit, T> { let Length(ref left, _) = self; let Length(ref right, _) = r; Length(*left + *right, PhantomData) } } fn main() { // Specialize one_foot to have hidden parameter `Inch` let one_foot: Length<Inch, f32> = Length(12.0, PhantomData); // one_meter has hidden parameter `Mm` let one_meter: Length<Mm, f32> = Length(1000.0, PhantomData); let two_feet = one_foot + one_foot; let two_meters = one_meter + one_meter; // Addition works println!("one foot + one_foot = {:?}", two_feet); println!("one meter + one_meter = {:?}", two_meters); // Nonsensical operations fail as they should // Error: type mismatch //let one_feter = one_foot + one_meter; }

See also:

Borrowing (&), Bounds (X: Y), enum, impl & self, Overloading, ref, Traits (X for Y), and TupleStructs.