1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
//! This module defines a trait `UpCastAs<T>` which allows one to upcast (as in only types which make sense //! and can fit it another are allowed) between primitive types. These follow a simple hierarchy: //! //! ``` //! f64 > f32 > u64 > u32 > u16 > u8 //! f64 > f32 > i64 > i32 > i16 > i8 //! ``` //! //! Signed and unsigned types don't mix well. You can see these as implication rules, as in a type //! which is `UpCastAs<u64>` implies it can be cast from `u32` since `u64 > u32`. And in this //! scheme, `UpCastAs<f64>` means it can be cast from a `f64`, which would mean it can be up cast //! from any number type. //! //! # Examples //! //! Examples of `cast`: //! //! ``` //! fn example<T: UpCastAs<u32>>() { //! let _: T = cast(10u8); //! let _ = cast::<u8, T>(10u8); // Alternate syntax, uglier. //! let _: T = cast(10u16); //! let _: T = cast(10u32); //! let _: T = cast(10u64); // Error, u64 > u32 //! let _: T = cast(10f32); // Error, f32 > u32 //! let _: T = cast(10f64); // Error, f32 > u32 //! } //! ``` //! //! `cast` is just a thin wrapper around `UpCastAs::from`: //! //! ``` //! fn example<T: UpCastAs<u32>>() { //! let _: T = UpCastAs::from(10u8); //! let _: T = UpCastAs::from(10u16); //! // ... //! } //! ``` //! //! You can also call from directly from `T`, *but it will not follow the implication rules*, it'll //! only recognize casting from `V` if `T: UpCastAs<V>`, so this is *not recommended*: //! //! ``` //! fn example<T: UpCastAs<u32>>() { //! let _ = T::from(10u16); // Error //! let _ = T::from(10u32); //! let _ = T::from(10u64); // Error. //! } //! ``` macro_rules! from_to { ($tr:ident, $f:ident, $t:ident) => { impl $tr<$f> for $t { fn from(x: $f) -> $t { x as $t } } } } pub trait UpCastAs<T> { fn from(T) -> Self; } macro_rules! cast_rule { ($b:ident as $a:ident) => ( impl UpCastAs<$a> for $b { #[inline(always)] fn from(t: $a) -> $b { t as $b } } ); ($a:ident => $b:ident) => ( impl<U: UpCastAs<$a>> UpCastAs<$b> for U { #[inline(always)] fn from(t: $b) -> U { U::from(t as $a) } } ); (self $a:ident) => ( impl UpCastAs<$a> for $a { #[inline(always)] fn from(t: $a) -> $a { t } } ) } cast_rule!(self u8); cast_rule!(self u16); cast_rule!(self u32); cast_rule!(self u64); cast_rule!(self i8); cast_rule!(self i16); cast_rule!(self i32); cast_rule!(self i64); cast_rule!(self f32); cast_rule!(self f64); // cast_rule!(u8 as u16); // cast_rule!(u8 as u32); // cast_rule!(u8 as u64); // cast_rule!(u8 as f32); // cast_rule!(u8 as f64); // Implications. Pyramid. cast_rule!(i16 => i8); cast_rule!(i32 => i16); cast_rule!(i64 => i32); cast_rule!(u16 => u8); cast_rule!(u32 => u16); cast_rule!(u64 => u32); cast_rule!(f32 => i64); cast_rule!(f32 => u64); cast_rule!(f64 => f32); #[inline(always)] pub fn cast<V, T: UpCastAs<V>>(v: V) -> T { UpCastAs::from(v) } #[cfg(test)] fn doit<T: UpCastAs<u64>>() { let _ = T::from(10u64); // let y = T::from(10u8); // Error. let _: T = cast(10u16); // Works for all types upscalable up to `B` where `T: UpCastAs<B>` // let _: T = cast(10f32); // Error let _ = cast::<u16, T>(10u16); // Alternate syntax. let _: T = UpCastAs::from(10u8); // Works for all types as well. }