Skip to content

Instantly share code, notes, and snippets.

@japaric
Created July 15, 2014 17:02
Show Gist options
  • Save japaric/8a942463b234bb9aebae to your computer and use it in GitHub Desktop.
Save japaric/8a942463b234bb9aebae to your computer and use it in GitHub Desktop.
Phantom types for unit checking
#![feature(macro_rules)]
use time::traits::{Minute,Second};
use length::traits::Meter;
use prefix::traits::{Micro,Mili,Nano,Kilo};
mod traits {
pub trait Prefix {
fn symbol(_: Option<Self>) -> &'static str;
fn exponent(_: Option<Self>) -> int;
}
pub trait Unit {
fn symbol(_: Option<Self>) -> &'static str;
}
}
macro_rules! unit {
($unit:ident, $symbol:expr) => {
pub enum $unit {}
impl Unit for $unit { fn symbol(_: Option<$unit>) -> &'static str { $symbol } }
}
}
mod length {
use std::fmt::{Formatter,Show};
use std::fmt;
use super::traits::Unit;
pub mod traits {
use super::Length;
use super::unit::Meter;
macro_rules! length {
($unit:ident, $method:ident) => {
pub trait $unit {
fn $method(self) -> Length<super::unit::$unit, Self> { Length(self) }
}
impl $unit for f32 {}
impl $unit for f64 {}
impl $unit for i16 {}
impl $unit for i32 {}
impl $unit for i64 {}
impl $unit for i8 {}
impl $unit for int {}
impl $unit for u16 {}
impl $unit for u32 {}
impl $unit for u64 {}
impl $unit for u8 {}
impl $unit for uint {}
// FIXME This should be equivalent to all the `impl` above this
//impl<T: Num> $unit for T {}
impl<T: Num> $unit for Vec<T> {}
impl<P, T> $unit for super::super::prefix::Prefix<P, T> {}
}
}
length!(Meter, meter)
}
mod unit {
use super::super::traits::Unit;
unit!(Meter, "m")
}
// FIXME Enforce `Unit` trait
//struct Length<U: super::traits::Unit, T>(T);
struct Length<U, T>(T);
impl<U: Unit, T: Show> Show for Length<U, T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let &Length(ref time) = self;
let u = Unit::symbol(None::<U>);
try!(time.fmt(f));
write!(f, "{}", u)
}
}
}
mod speed {
// FIXME Enforce `Unit` trait
//struct Speed<U: super::traits::Unit, T>(T);
pub struct Speed<U, T>(T);
}
mod time {
use super::traits::Unit;
use std::fmt::{Formatter,Show};
use std::fmt;
pub mod traits {
use super::Time;
use super::unit::{Minute,Second};
macro_rules! time {
($unit:ident, $method:ident) => {
pub trait $unit {
fn $method(self) -> Time<super::unit::$unit, Self> { Time(self) }
}
impl $unit for f32 {}
impl $unit for f64 {}
impl $unit for i16 {}
impl $unit for i32 {}
impl $unit for i64 {}
impl $unit for i8 {}
impl $unit for int {}
impl $unit for u16 {}
impl $unit for u32 {}
impl $unit for u64 {}
impl $unit for u8 {}
impl $unit for uint {}
impl<T> $unit for Vec<T> {}
impl<P, T> $unit for super::super::prefix::Prefix<P, T> {}
}
}
time!(Second, second)
time!(Minute, minute)
}
mod unit {
use super::super::traits::Unit;
unit!(Second, "s")
unit!(Minute, "m")
unit!(Hour, "h")
unit!(Day, "d")
}
// FIXME Enforce `Unit` trait
//struct Length<U: super::traits::Unit, T>(T);
struct Time<U, T>(T);
impl<U: Unit, T: Show> Show for Time<U, T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let &Time(ref time) = self;
let u = Unit::symbol(None::<U>);
try!(time.fmt(f));
write!(f, "{}", u)
}
}
}
mod prefix {
use std::fmt::{Formatter,Show};
use std::fmt;
pub mod traits {
macro_rules! prefix {
($prefix:ident, $method:ident) => {
pub trait $prefix {
fn $method(self) -> super::Prefix<super::$prefix, Self> { super::Prefix(self) }
}
impl $prefix for f32 {}
impl $prefix for f64 {}
impl $prefix for i16 {}
impl $prefix for i32 {}
impl $prefix for i64 {}
impl $prefix for i8 {}
impl $prefix for int {}
impl $prefix for u16 {}
impl $prefix for u32 {}
impl $prefix for u64 {}
impl $prefix for u8 {}
impl $prefix for uint {}
// FIXME This should be equivalent to all the `impl` above this
//impl<T: Num> $prefix for T {}
impl<T: Num> $prefix for Vec<T> {}
}
}
prefix!(Nano, nano)
prefix!(Micro, micro)
prefix!(Mili, mili)
prefix!(Kilo, kilo)
}
// FIXME Enforce `Prefix` trait
//struct Prefix<U: super::traits::Prefix, T>(T);
pub struct Prefix<P,T>(T);
impl<P, T> Prefix<P, T> {
fn unwrap(self) -> T {
let Prefix(prefix) = self;
prefix
}
fn get_ref<'a>(&'a self) -> &'a T {
let &Prefix(ref prefix) = self;
prefix
}
}
impl<
P,
T: Div<U, V>,
U,
V
> Div<U, Prefix<P, V>>
for Prefix<P, T> {
fn div(&self, rhs: &U) -> Prefix<P, V> {
Prefix(self.get_ref().div(rhs))
}
}
// TODO Commutative part: T * Prefix = Prefix
impl<
P,
T: Mul<T, T>
> Mul<T, Prefix<P, T>>
for Prefix<P, T> {
fn mul(&self, rhs: &T) -> Prefix<P, T> {
Prefix(self.get_ref().mul(rhs))
}
}
// FIXME Conflicting implementation!
//impl<
//T: Mul<T, T>
//> Mul<Prefix<Mili, T>, T>
//for Prefix<Kilo, T> {
//fn mul(&self, rhs: &Prefix<Mili, T>) -> T {
//self.get_ref().mul(rhs.get_ref())
//}
//}
impl<P: super::traits::Prefix, T: Show> Show for Prefix<P, T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let &Prefix(ref prefix) = self;
let p = super::traits::Prefix::symbol(None::<P>);
try!(prefix.fmt(f));
write!(f, "{}", p)
}
}
macro_rules! prefix {
($prefix:ident, $symbol:expr, $exponent:expr) => {
enum $prefix {}
impl super::traits::Prefix for $prefix {
fn symbol(_: Option<$prefix>) -> &'static str { $symbol }
fn exponent(_: Option<$prefix>) -> int { $exponent }
}
}
}
prefix!(Nano, "n", -9)
prefix!(Micro, "u", -6)
prefix!(Mili, "m", -3)
prefix!(Kilo, "K", 3)
}
fn main() {
let length = 5u.kilo().meter();
println!("{}", length);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment