Created
July 15, 2014 17:02
-
-
Save japaric/8a942463b234bb9aebae to your computer and use it in GitHub Desktop.
Phantom types for unit checking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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