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