better_as/
number.rs

1#![allow(clippy::use_self)]
2
3#[cfg(not(any(
4    target_pointer_width = "16",
5    target_pointer_width = "32",
6    target_pointer_width = "64",
7)))]
8compile_error!("unsupported pointer width");
9
10use core::convert::Infallible;
11use core::fmt::{self, Display};
12
13pub fn checked_cast<T, U>(src: T) -> Result<U, T::Error>
14where
15    T: CheckedCast<U>,
16{
17    src.checked_cast()
18}
19
20pub fn wrapping_cast<T, U>(src: T) -> U
21where
22    T: WrappingCast<U>,
23{
24    src.wrapping_cast()
25}
26
27pub fn extending_cast<T, U>(src: T) -> U
28where
29    T: ExtendingCast<U>,
30{
31    src.extending_cast()
32}
33
34pub fn truncating_cast<T, U>(src: T) -> U
35where
36    T: TruncatingCast<U>,
37{
38    src.truncating_cast()
39}
40
41pub trait CheckedCast<T> {
42    type Error;
43    fn checked_cast(self) -> Result<T, Self::Error>;
44}
45
46pub trait WrappingCast<T> {
47    fn wrapping_cast(self) -> T;
48}
49
50pub trait ExtendingCast<T> {
51    fn extending_cast(self) -> T;
52}
53
54pub trait TruncatingCast<T> {
55    fn truncating_cast(self) -> T;
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum NumCastError {
60    Overflow,
61    Underflow,
62    Infinite,
63    NaN,
64    Fractional,
65}
66
67impl Display for NumCastError {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        let msg = match *self {
70            Self::Overflow => "Overflow during numeric conversion",
71            Self::Underflow => "Underflow during numeric conversion",
72            Self::Fractional => "Cannot store fractional value without loss",
73            Self::Infinite => "Cannot store infinite value in finite type",
74            Self::NaN => "Cannot store NaN in type which does not support it",
75        };
76
77        f.write_str(msg)
78    }
79}
80
81#[cfg(feature = "std")]
82impl std::error::Error for NumCastError {}
83
84macro_rules! check {
85    (upper $val: expr, $rhs: ty) => {
86        if $val > <$rhs>::MAX as Self {
87            return Err(NumCastError::Overflow);
88        }
89    };
90    (lower $val: expr, $rhs: ty) => {
91        if $val < <$rhs>::MIN as Self {
92            return Err(NumCastError::Underflow);
93        }
94    };
95    (both $val: expr, $rhs: ty) => {
96        check!(upper $val, $rhs);
97        check!(lower $val, $rhs);
98    };
99    (infallible $val: expr, $rhs: ty) => {}
100}
101
102macro_rules! checked_cast{
103    ($lhs:ty => $rhs:ty: upper) => {
104        impl CheckedCast<$rhs> for $lhs {
105            type Error = NumCastError;
106            fn checked_cast(self) -> Result<$rhs, Self::Error> {
107                check!(upper self, $rhs);
108                Ok(self as $rhs)
109            }
110        }
111    };
112    ($lhs:ty => $rhs:ty: lower) => {
113        impl CheckedCast<$rhs> for $lhs {
114            type Error = NumCastError;
115            fn checked_cast(self) -> Result<$rhs, Self::Error> {
116                check!(lower self, $rhs);
117                Ok(self as $rhs)
118            }
119        }
120    };
121    ($lhs:ty => $rhs:ty: both) => {
122        impl CheckedCast<$rhs> for $lhs {
123            type Error = NumCastError;
124            fn checked_cast(self) -> Result<$rhs, Self::Error> {
125                check!(both self, $rhs);
126                Ok(self as $rhs)
127            }
128        }
129    };
130    ($lhs:ty => $rhs:ty: infallible ) => {
131        impl CheckedCast<$rhs> for $lhs {
132            type Error = Infallible;
133            fn checked_cast(self) -> Result<$rhs, Self::Error> {
134                Ok(self as $rhs)
135            }
136        }
137    };
138
139    ($lhs: ty => $rhs: ty: 16:$c16:tt, 32:$c32:tt, 64:$c64:tt) => {
140        impl CheckedCast<$rhs> for $lhs {
141            type Error = NumCastError;
142
143            fn checked_cast(self) -> Result<$rhs, Self::Error> {
144                #[cfg(target_pointer_width = "16")]
145                {
146                    check!($c16 self, $rhs);
147                }
148                #[cfg(target_pointer_width = "32")]
149                {
150                    check!($c32 self, $rhs);
151                }
152                #[cfg(target_pointer_width = "64")]
153                {
154                    check!($c64 self, $rhs);
155                }
156                Ok(self as $rhs)
157            }
158        }
159    };
160    (f32 => $rhs: ty) => {
161        impl CheckedCast<$rhs> for f32 {
162            type Error = NumCastError;
163
164            fn checked_cast(self) -> Result<$rhs, Self::Error> {
165                if self.is_nan() {
166                    return Err(NumCastError::NaN);
167                }
168                if self.is_infinite() {
169                    return Err(NumCastError::Infinite);
170                }
171                if self.trunc().to_bits() != self.to_bits() {
172                    return Err(NumCastError::Fractional);
173                }
174                check!(both self, $rhs);
175                Ok(self as $rhs)
176            }
177        }
178    };
179    (f64 => $rhs: ty) => {
180        impl CheckedCast<$rhs> for f64 {
181            type Error = NumCastError;
182
183            fn checked_cast(self) -> Result<$rhs, Self::Error> {
184                if self.is_nan() {
185                    return Err(NumCastError::NaN);
186                }
187                if self.is_infinite() {
188                    return Err(NumCastError::Infinite);
189                }
190                if self.trunc().to_bits() != self.to_bits() {
191                    return Err(NumCastError::Fractional);
192                }
193                check!(both self, $rhs);
194                Ok(self as $rhs)
195            }
196        }
197    }
198}
199
200// u8
201checked_cast!(u8 => u8     : infallible  );
202checked_cast!(u8 => u16    : infallible  );
203checked_cast!(u8 => u32    : infallible  );
204checked_cast!(u8 => u64    : infallible  );
205checked_cast!(u8 => u128   : infallible  );
206checked_cast!(u8 => i8     : upper       );
207checked_cast!(u8 => i16    : infallible  );
208checked_cast!(u8 => i32    : infallible  );
209checked_cast!(u8 => i64    : infallible  );
210checked_cast!(u8 => i128   : infallible  );
211checked_cast!(u8 => usize  : infallible  );
212checked_cast!(u8 => isize  : infallible  );
213checked_cast!(u8 => f32    : infallible  );
214checked_cast!(u8 => f64    : infallible  );
215
216// u16
217checked_cast!(u16 => u8    : upper       );
218checked_cast!(u16 => u16   : infallible  );
219checked_cast!(u16 => u32   : infallible  );
220checked_cast!(u16 => u64   : infallible  );
221checked_cast!(u16 => u128  : infallible  );
222checked_cast!(u16 => i8    : upper       );
223checked_cast!(u16 => i16   : upper       );
224checked_cast!(u16 => i32   : infallible  );
225checked_cast!(u16 => i64   : infallible  );
226checked_cast!(u16 => i128  : infallible  );
227checked_cast!(u16 => usize : infallible  );
228checked_cast!(u16 => isize : 16: upper, 32: infallible, 64: infallible       );
229checked_cast!(u16 => f32   : infallible  );
230checked_cast!(u16 => f64   : infallible  );
231
232// u32
233checked_cast!(u32 => u8    : upper       );
234checked_cast!(u32 => u16   : upper       );
235checked_cast!(u32 => u32   : infallible  );
236checked_cast!(u32 => u64   : infallible  );
237checked_cast!(u32 => u128  : infallible  );
238checked_cast!(u32 => i8    : upper       );
239checked_cast!(u32 => i16   : upper       );
240checked_cast!(u32 => i32   : upper       );
241checked_cast!(u32 => i64   : infallible  );
242checked_cast!(u32 => i128  : infallible  );
243checked_cast!(u32 => usize : 16: upper, 32: infallible, 64: infallible       );
244checked_cast!(u32 => isize : 16: upper, 32: upper     , 64: infallible       );
245
246// u64
247checked_cast!(u64 => u8    : upper       );
248checked_cast!(u64 => u16   : upper       );
249checked_cast!(u64 => u32   : upper       );
250checked_cast!(u64 => u64   : infallible  );
251checked_cast!(u64 => u128  : infallible  );
252checked_cast!(u64 => i8    : upper       );
253checked_cast!(u64 => i16   : upper       );
254checked_cast!(u64 => i32   : upper       );
255checked_cast!(u64 => i64   : upper       );
256checked_cast!(u64 => i128  : infallible  );
257checked_cast!(u64 => usize : 16: upper, 32: upper     , 64: infallible       );
258checked_cast!(u64 => isize : upper       );
259
260// u128
261checked_cast!(u128 => u8    : upper       );
262checked_cast!(u128 => u16   : upper       );
263checked_cast!(u128 => u32   : upper       );
264checked_cast!(u128 => u64   : upper       );
265checked_cast!(u128 => u128  : infallible  );
266checked_cast!(u128 => i8    : upper       );
267checked_cast!(u128 => i16   : upper       );
268checked_cast!(u128 => i32   : upper       );
269checked_cast!(u128 => i64   : upper       );
270checked_cast!(u128 => i128  : upper       );
271checked_cast!(u128 => usize : upper       );
272checked_cast!(u128 => isize : upper       );
273
274// i8
275checked_cast!(i8 => u8     : lower       );
276checked_cast!(i8 => u16    : lower       );
277checked_cast!(i8 => u32    : lower       );
278checked_cast!(i8 => u64    : lower       );
279checked_cast!(i8 => u128   : lower       );
280checked_cast!(i8 => i8     : infallible  );
281checked_cast!(i8 => i16    : infallible  );
282checked_cast!(i8 => i32    : infallible  );
283checked_cast!(i8 => i64    : infallible  );
284checked_cast!(i8 => i128   : infallible  );
285checked_cast!(i8 => usize  : lower       );
286checked_cast!(i8 => isize  : infallible  );
287checked_cast!(i8 => f32   : infallible  );
288checked_cast!(i8 => f64   : infallible  );
289
290// i16
291checked_cast!(i16 => u8    : both        );
292checked_cast!(i16 => u16   : lower       );
293checked_cast!(i16 => u32   : lower       );
294checked_cast!(i16 => u64   : lower       );
295checked_cast!(i16 => u128  : lower       );
296checked_cast!(i16 => i8    : both        );
297checked_cast!(i16 => i16   : infallible  );
298checked_cast!(i16 => i32   : infallible  );
299checked_cast!(i16 => i64   : infallible  );
300checked_cast!(i16 => i128  : infallible  );
301checked_cast!(i16 => usize : lower       );
302checked_cast!(i16 => isize : infallible  );
303checked_cast!(i16 => f32   : infallible  );
304checked_cast!(i16 => f64   : infallible  );
305
306// i32
307checked_cast!(i32 => u8    : both        );
308checked_cast!(i32 => u16   : both        );
309checked_cast!(i32 => u32   : lower       );
310checked_cast!(i32 => u64   : lower       );
311checked_cast!(i32 => u128  : lower       );
312checked_cast!(i32 => i8    : both        );
313checked_cast!(i32 => i16   : both        );
314checked_cast!(i32 => i32   : infallible  );
315checked_cast!(i32 => i64   : infallible  );
316checked_cast!(i32 => i128  : infallible  );
317checked_cast!(i32 => usize : 16: both,   32: lower      , 64: lower             );
318checked_cast!(i32 => isize : 16: both,   32: infallible , 64: infallible        );
319
320// i64
321checked_cast!(i64 => u8    : both        );
322checked_cast!(i64 => u16   : both        );
323checked_cast!(i64 => u32   : both        );
324checked_cast!(i64 => u64   : lower       );
325checked_cast!(i64 => u128  : lower       );
326checked_cast!(i64 => i8    : both        );
327checked_cast!(i64 => i16   : both        );
328checked_cast!(i64 => i32   : both        );
329checked_cast!(i64 => i64   : infallible  );
330checked_cast!(i64 => i128  : infallible  );
331checked_cast!(i64 => usize : 16: both,   32: both      , 64: lower             );
332checked_cast!(i64 => isize : 16: both,   32: both      , 64: infallible        );
333
334// i128
335checked_cast!(i128 => u8   : both        );
336checked_cast!(i128 => u16  : both        );
337checked_cast!(i128 => u32  : both        );
338checked_cast!(i128 => u64  : both        );
339checked_cast!(i128 => u128 : lower       );
340checked_cast!(i128 => i8   : both        );
341checked_cast!(i128 => i16  : both        );
342checked_cast!(i128 => i32  : both        );
343checked_cast!(i128 => i64  : both        );
344checked_cast!(i128 => i128 : infallible  );
345checked_cast!(i128 => usize: both        );
346checked_cast!(i128 => isize: both        );
347
348// usize
349checked_cast!(usize => u8   : upper                                              );
350checked_cast!(usize => u16  : 16: infallible, 32: upper     , 64: upper          );
351checked_cast!(usize => u32  : 16: infallible, 32: infallible, 64: upper          );
352checked_cast!(usize => u64  : 16: infallible, 32: infallible, 64: infallible     );
353checked_cast!(usize => u128 : 16: infallible, 32: infallible, 64: infallible     );
354checked_cast!(usize => i8   : upper                                              );
355checked_cast!(usize => i16  : upper                                              );
356checked_cast!(usize => i32  : 16: infallible, 32: upper     , 64: upper          );
357checked_cast!(usize => i64  : 16: infallible, 32: infallible, 64: upper          );
358checked_cast!(usize => i128 : 16: infallible, 32: infallible, 64: infallible     );
359checked_cast!(usize => usize: infallible                                         );
360checked_cast!(usize => isize: upper                                              );
361
362// isize
363checked_cast!(isize => u8   : both                                               );
364checked_cast!(isize => u16  : 16: lower     , 32: both       , 64: both          );
365checked_cast!(isize => u32  : 16: lower     , 32: lower      , 64: both          );
366checked_cast!(isize => u64  : lower                                              );
367checked_cast!(isize => u128 : lower                                              );
368checked_cast!(isize => i8   : both                                               );
369checked_cast!(isize => i16  : 16: infallible, 32: both      , 64: both           );
370checked_cast!(isize => i32  : 16: infallible, 32: infallible, 64: both           );
371checked_cast!(isize => i64  : 16: infallible, 32: infallible, 64: infallible     );
372checked_cast!(isize => i128 : 16: infallible, 32: infallible, 64: infallible     );
373checked_cast!(isize => usize: lower                                              );
374checked_cast!(isize => isize: infallible                                         );
375
376// f32
377checked_cast!(f32 => u8);
378checked_cast!(f32 => u16);
379checked_cast!(f32 => i8);
380checked_cast!(f32 => i16);
381checked_cast!(f32 => f32: infallible);
382checked_cast!(f32 => f64: infallible);
383
384// f64
385checked_cast!(f64 => u8);
386checked_cast!(f64 => u16);
387checked_cast!(f64 => i8);
388checked_cast!(f64 => i16);
389checked_cast!(f64 => f64: infallible);
390
391macro_rules! wrapping_cast {
392    ($lhs: ty=>$rhs:ty) => {
393        impl WrappingCast<$rhs> for $lhs {
394            fn wrapping_cast(self) -> $rhs {
395                self as $rhs
396            }
397        }
398    };
399}
400
401wrapping_cast!(u8    => i8   );
402wrapping_cast!(u16   => i16  );
403wrapping_cast!(u32   => i32  );
404wrapping_cast!(u64   => i64  );
405wrapping_cast!(u128  => i128 );
406wrapping_cast!(usize => isize);
407wrapping_cast!(i8    => u8   );
408wrapping_cast!(i16   => u16  );
409wrapping_cast!(i32   => u32  );
410wrapping_cast!(i64   => u64  );
411wrapping_cast!(i128  => u128 );
412wrapping_cast!(isize => usize);
413
414macro_rules! extending_cast {
415    ($lhs: ty=>$rhs:ty) => {
416        impl ExtendingCast<$rhs> for $lhs {
417            fn extending_cast(self) -> $rhs {
418                self as $rhs
419            }
420        }
421    };
422}
423
424extending_cast!(u8  => u16  );
425extending_cast!(u8  => u32  );
426extending_cast!(u8  => u64  );
427extending_cast!(u8  => u128 );
428extending_cast!(u16 => u32  );
429extending_cast!(u16 => u64  );
430extending_cast!(u16 => u128 );
431extending_cast!(u32 => u64  );
432extending_cast!(u32 => u128 );
433extending_cast!(u64 => u128 );
434extending_cast!(i8  => i16  );
435extending_cast!(i8  => i32  );
436extending_cast!(i8  => i64  );
437extending_cast!(i8  => i128 );
438extending_cast!(i16 => i32  );
439extending_cast!(i16 => i64  );
440extending_cast!(i16 => i128 );
441extending_cast!(i32 => i64  );
442extending_cast!(i32 => i128 );
443extending_cast!(i64 => i128 );
444
445macro_rules! truncating_cast {
446    ($lhs: ty=>$rhs:ty) => {
447        impl TruncatingCast<$rhs> for $lhs {
448            fn truncating_cast(self) -> $rhs {
449                self as $rhs
450            }
451        }
452    };
453}
454
455truncating_cast!(u16  => u8  );
456truncating_cast!(u32  => u8  );
457truncating_cast!(u64  => u8  );
458truncating_cast!(u128 => u8  );
459truncating_cast!(u32  => u16 );
460truncating_cast!(u64  => u16 );
461truncating_cast!(u128 => u16 );
462truncating_cast!(u64  => u32 );
463truncating_cast!(u128 => u32 );
464truncating_cast!(u128 => u64 );
465truncating_cast!(i16  => i8  );
466truncating_cast!(i32  => i8  );
467truncating_cast!(i64  => i8  );
468truncating_cast!(i128 => i8  );
469truncating_cast!(i32  => i16 );
470truncating_cast!(i64  => i16 );
471truncating_cast!(i128 => i16 );
472truncating_cast!(i64  => i32 );
473truncating_cast!(i128 => i32 );
474truncating_cast!(i128 => i64 );