cast/
lib.rs

1//! Ergonomic, checked cast functions for primitive types
2//!
3//! This crate provides one checked cast function for each numeric primitive.
4//! Use these functions to perform a cast from any other numeric primitive:
5//!
6//! ```
7//! use cast::{u8, u16, Error};
8//!
9//! # fn main() {
10//! // Infallible operations, like integer promotion, are equivalent to a normal
11//! // cast with `as`
12//! assert_eq!(u16(0u8), 0u16);
13//!
14//! // Everything else will return a `Result` depending on the success of the
15//! // operation
16//! assert_eq!(u8(0u16), Ok(0u8));
17//! assert_eq!(u8(256u16), Err(Error::Overflow));
18//! assert_eq!(u8(-1i8), Err(Error::Underflow));
19//! assert_eq!(u8(1. / 0.), Err(Error::Infinite));
20//! assert_eq!(u8(0. / 0.), Err(Error::NaN));
21//! # }
22//! ```
23//!
24//! There are no namespace problems between these functions, the "primitive
25//! modules" in `core`/`std` and the built-in primitive types, so all them can
26//! be in the same scope:
27//!
28//! ```
29//! use std::u8;
30//! use cast::{u8, u16};
31//!
32//! # fn main() {
33//! // `u8` as a type
34//! let x: u8 = 0;
35//! // `u8` as a module
36//! let y = u16(u8::MAX);
37//! // `u8` as a function
38//! let z = u8(y).unwrap();
39//! # }
40//! ```
41//!
42//! The checked cast functionality is also usable with type aliases via the
43//! `cast` static method:
44//!
45//! ```
46//! use std::os::raw::c_ulonglong;
47//! // NOTE avoid shadowing `std::convert::From` - cf. rust-lang/rfcs#1311
48//! use cast::From as _0;
49//!
50//! # fn main() {
51//! assert_eq!(c_ulonglong::cast(0u8), 0u64);
52//! # }
53//! ```
54//!
55//! This crate also provides a `From` trait that can be used, for example,
56//! to create a generic function that accepts any type that can be infallibly
57//! casted to `u32`.
58//!
59//! ```
60//! fn to_u32<T>(x: T) -> u32
61//!     // reads as: "where u32 can be casted from T with output u32"
62//!     where u32: cast::From<T, Output=u32>,
63//! {
64//!     cast::u32(x)
65//! }
66//!
67//! # fn main() {
68//! assert_eq!(to_u32(0u8), 0u32);
69//! assert_eq!(to_u32(1u16), 1u32);
70//! assert_eq!(to_u32(2u32), 2u32);
71//!
72//! // to_u32(-1i32);  // Compile error
73//! # }
74//! ```
75//!
76//! ## Minimal Supported Rust Version
77//!
78//! This crate is guaranteed to compile *as a dependency* on stable Rust 1.31 and up.
79//! It's not guaranteed that `cargo test`-ing this crate follows the MSRV.
80//! It *might* compile on older versions but that may change in any new patch release.
81//!
82//! ## Building without `std`
83//!
84//! This crate can be used without Rust's `std` crate by declaring it as
85//! follows in your `Cargo.toml`:
86//!
87//! ``` toml
88//! cast = { version = "*", default-features = false }
89//! ```
90
91#![allow(const_err)]
92#![cfg_attr(not(feature = "std"), no_std)]
93#![deny(missing_docs)]
94#![deny(unsafe_code)]
95#![deny(warnings)]
96
97#[cfg(test)]
98#[macro_use]
99extern crate quickcheck;
100
101use core::fmt;
102#[cfg(feature = "std")]
103use std::error;
104
105#[cfg(test)]
106mod test;
107
108/// Cast errors
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110pub enum Error {
111    /// Infinite value casted to a type that can only represent finite values
112    Infinite,
113    /// NaN value casted to a type that can't represent a NaN value
114    NaN,
115    /// Source value is greater than the maximum value that the destination type
116    /// can hold
117    Overflow,
118    /// Source value is smaller than the minimum value that the destination type
119    /// can hold
120    Underflow,
121}
122
123impl Error {
124    /// A private helper function that implements `description`, because
125    /// `description` is only available when we have `std` enabled.
126    fn description_helper(&self) -> &str {
127        match *self {
128            Error::Infinite => "Cannot store infinite value in finite type",
129            Error::NaN => "Cannot store NaN in type which does not support it",
130            Error::Overflow => "Overflow during numeric conversion",
131            Error::Underflow => "Underflow during numeric conversion",
132        }
133    }
134}
135
136impl fmt::Display for Error {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        write!(f, "{}", self.description_helper())
139    }
140}
141
142#[cfg(feature = "std")]
143impl error::Error for Error {
144    fn description(&self) -> &str {
145        self.description_helper()
146    }
147}
148
149/// The "cast from" operation
150pub trait From<Src> {
151    /// The result of the cast operation: either `Self` or `Result<Self, Error>`
152    type Output;
153
154    /// Checked cast from `Src` to `Self`
155    fn cast(_: Src) -> Self::Output;
156}
157
158macro_rules! fns {
159    ($($ty:ident),+) => {
160        $(
161            /// Checked cast function
162            #[inline]
163            pub fn $ty<T>(x: T) -> <$ty as From<T>>::Output
164                where $ty: From<T>
165            {
166                <$ty as From<T>>::cast(x)
167            }
168         )+
169    }
170}
171
172fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
173
174fns!(i128, u128);
175
176/// `$dst` can hold any value of `$src`
177macro_rules! promotion {
178    ($($src:ty => $($dst: ty),+);+;) => {
179        $(
180            $(
181                impl From<$src> for $dst {
182                    type Output = $dst;
183
184                    #[inline]
185                    fn cast(src: $src) -> $dst {
186                        src as $dst
187                    }
188                }
189            )+
190        )+
191    }
192}
193
194/// `$dst` can hold any positive value of `$src`
195macro_rules! half_promotion {
196    ($($src:ty => $($dst:ty),+);+;) => {
197        $(
198            $(
199                impl From<$src> for $dst {
200                    type Output = Result<$dst, Error>;
201
202                    #[inline]
203                    fn cast(src: $src) -> Self::Output {
204                        if src < 0 {
205                            Err(Error::Underflow)
206                        } else {
207                            Ok(src as $dst)
208                        }
209                    }
210                }
211            )+
212        )+
213    }
214}
215
216/// From an unsigned `$src` to a smaller `$dst`
217macro_rules! from_unsigned {
218    ($($src:ident => $($dst:ident),+);+;) => {
219        $(
220            $(
221                impl From<$src> for $dst {
222                    type Output = Result<$dst, Error>;
223
224                    #[inline]
225                    fn cast(src: $src) -> Self::Output {
226                        use core::$dst;
227
228                        if src > $dst::MAX as $src {
229                            Err(Error::Overflow)
230                        } else {
231                            Ok(src as $dst)
232                        }
233                    }
234                }
235            )+
236        )+
237    }
238}
239
240/// From a signed `$src` to a smaller `$dst`
241macro_rules! from_signed {
242    ($($src:ident => $($dst:ident),+);+;) => {
243        $(
244            $(
245                impl From<$src> for $dst {
246                    type Output = Result<$dst, Error>;
247
248                    #[inline]
249                    fn cast(src: $src) -> Self::Output {
250                        use core::$dst;
251
252                        Err(if src < $dst::MIN as $src {
253                            Error::Underflow
254                        } else if src > $dst::MAX as $src {
255                            Error::Overflow
256                        } else {
257                            return Ok(src as $dst);
258                        })
259                    }
260                }
261            )+
262        )+
263    }
264}
265
266/// From a float `$src` to an integer `$dst`
267macro_rules! from_float {
268    ($($src:ident => $($dst:ident),+);+;) => {
269        $(
270            $(
271                impl From<$src> for $dst {
272                    type Output = Result<$dst, Error>;
273
274                    #[inline]
275                    fn cast(src: $src) -> Self::Output {
276                        use core::{$dst, $src};
277
278                        Err(if src != src {
279                            Error::NaN
280                        } else if src == $src::INFINITY ||
281                            src == $src::NEG_INFINITY {
282                            Error::Infinite
283                        } else if {
284                            // this '$dst::BITS' works on 1.31.0 (MSRV)
285                            let dst_bits = core::mem::size_of::<$dst>() as u32 * 8;
286                            let lossless = dst_bits < core::$src::MANTISSA_DIGITS;
287
288                            let max = if lossless {
289                                $dst::MAX as $src
290                            } else {
291                                // we subtract 1 ULP (unit of least precision) here because some
292                                // lossy conversions like `u64::MAX as f64` round *up* and we want
293                                // to avoid the check below evaluating to false in that case
294                                $src::from_bits(($dst::MAX as $src).to_bits() - 1)
295                            };
296
297                            src > max
298                        } {
299                            Error::Overflow
300                        } else if $dst::MIN == 0 {
301                            // when casting to unsigned integer, negative values close to 0 but
302                            // larger than 1.0 should be truncated to 0; this behavior matches
303                            // casting from a float to a signed integer
304                            if src <= -1.0 {
305                                Error::Underflow
306                            } else {
307                                return Ok(src as $dst);
308                            }
309                        } else if src < $dst::MIN as $src {
310                            Error::Underflow
311                        } else  {
312                            return Ok(src as $dst);
313                        })
314                    }
315                }
316            )+
317        )+
318    }
319}
320
321/// From a float `$src` to an integer `$dst`, where $dst is large enough to contain
322/// all values of `$src`. We can't ever overflow here
323macro_rules! from_float_dst {
324    ($($src:ident => $($dst:ident),+);+;) => {
325        $(
326            $(
327                impl From<$src> for $dst {
328                     type Output = Result<$dst, Error>;
329
330                    #[inline]
331                    #[allow(unused_comparisons)]
332                    fn cast(src: $src) -> Self::Output {
333                        use core::{$dst, $src};
334
335                        Err(if src != src {
336                            Error::NaN
337                        } else if src == $src::INFINITY ||
338                            src == $src::NEG_INFINITY {
339                            Error::Infinite
340                        } else if ($dst::MIN == 0) && src <= -1.0 {
341                            Error::Underflow
342                        } else {
343                            return Ok(src as $dst);
344                        })
345                    }
346                }
347            )+
348        )+
349    }
350}
351
352// PLAY TETRIS! ;-)
353
354#[cfg(target_pointer_width = "32")]
355mod _32 {
356    use crate::{Error, From};
357
358    // Signed
359    promotion! {
360        i8    => f32, f64, i8, i16, i32, isize, i64;
361        i16   => f32, f64,     i16, i32, isize, i64;
362        i32   => f32, f64,          i32, isize, i64;
363        isize => f32, f64,          i32, isize, i64;
364        i64   => f32, f64,                      i64;
365    }
366
367    half_promotion! {
368        i8    =>                                     u8, u16, u32, usize, u64;
369        i16   =>                                         u16, u32, usize, u64;
370        i32   =>                                              u32, usize, u64;
371        isize =>                                              u32, usize, u64;
372        i64   =>                                                          u64;
373    }
374
375    from_signed! {
376
377        i16   =>           i8,                       u8;
378        i32   =>           i8, i16,                  u8, u16;
379        isize =>           i8, i16,                  u8, u16;
380        i64   =>           i8, i16, i32, isize,      u8, u16, u32, usize;
381    }
382
383    // Unsigned
384    promotion! {
385        u8    => f32, f64,     i16, i32, isize, i64, u8, u16, u32, usize, u64;
386        u16   => f32, f64,          i32, isize, i64,     u16, u32, usize, u64;
387        u32   => f32, f64,                      i64,          u32, usize, u64;
388        usize => f32, f64,                      i64,          u32, usize, u64;
389        u64   => f32, f64,                                                u64;
390    }
391
392    from_unsigned! {
393        u8    =>           i8;
394        u16   =>           i8, i16,                  u8;
395        u32   =>           i8, i16, i32, isize,      u8, u16;
396        usize =>           i8, i16, i32, isize,      u8, u16;
397        u64   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize;
398    }
399
400    // Float
401    promotion! {
402        f32   => f32, f64;
403        f64   =>      f64;
404    }
405
406    from_float! {
407        f32 =>             i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
408        f64 =>             i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
409    }
410}
411
412#[cfg(target_pointer_width = "64")]
413mod _64 {
414    use crate::{Error, From};
415
416    // Signed
417    promotion! {
418        i8    => f32, f64, i8, i16, i32, i64, isize;
419        i16   => f32, f64,     i16, i32, i64, isize;
420        i32   => f32, f64,          i32, i64, isize;
421        i64   => f32, f64,               i64, isize;
422        isize => f32, f64,               i64, isize;
423    }
424
425    half_promotion! {
426        i8    =>                                     u8, u16, u32, u64, usize;
427        i16   =>                                         u16, u32, u64, usize;
428        i32   =>                                              u32, u64, usize;
429        i64   =>                                                   u64, usize;
430        isize =>                                                   u64, usize;
431    }
432
433    from_signed! {
434
435        i16   =>           i8,                       u8;
436        i32   =>           i8, i16,                  u8, u16;
437        i64   =>           i8, i16, i32,             u8, u16, u32;
438        isize =>           i8, i16, i32,             u8, u16, u32;
439    }
440
441    // Unsigned
442    promotion! {
443        u8    => f32, f64,     i16, i32, i64, isize, u8, u16, u32, u64, usize;
444        u16   => f32, f64,          i32, i64, isize,     u16, u32, u64, usize;
445        u32   => f32, f64,               i64, isize,          u32, u64, usize;
446        u64   => f32, f64,                                         u64, usize;
447        usize => f32, f64,                                         u64, usize;
448    }
449
450    from_unsigned! {
451        u8    =>           i8;
452        u16   =>           i8, i16,                  u8;
453        u32   =>           i8, i16, i32,             u8, u16;
454        u64   =>           i8, i16, i32, i64, isize, u8, u16, u32;
455        usize =>           i8, i16, i32, i64, isize, u8, u16, u32;
456    }
457
458    // Float
459    promotion! {
460        f32  => f32, f64;
461        f64  =>      f64;
462    }
463
464    from_float! {
465        f32 =>             i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
466        f64 =>             i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
467    }
468}
469
470mod _x128 {
471    use crate::{Error, From};
472
473    // Signed
474    promotion! {
475        i8    =>                              i128;
476        i16   =>                              i128;
477        i32   =>                              i128;
478        i64   =>                              i128;
479        isize =>                              i128;
480        i128  => f32, f64,                    i128;
481    }
482
483    half_promotion! {
484        i8    =>                                                              u128;
485        i16   =>                                                              u128;
486        i32   =>                                                              u128;
487        i64   =>                                                              u128;
488        isize =>                                                              u128;
489        i128  =>                                                              u128;
490    }
491
492    from_signed! {
493        i128  =>           i8, i16, i32, i64,       isize, u8, u16, u32, u64,       usize;
494    }
495
496    // Unsigned
497    promotion! {
498        u8    =>                              i128,                           u128;
499        u16   =>                              i128,                           u128;
500        u32   =>                              i128,                           u128;
501        u64   =>                              i128,                           u128;
502        usize =>                              i128,                           u128;
503        u128  =>      f64,                                                    u128;
504    }
505
506    from_unsigned! {
507        u128 => f32,       i8, i16, i32, i64, i128, isize, u8, u16, u32, u64,       usize;
508    }
509
510    // Float
511    from_float_dst! {
512        f32 =>                                                                u128;
513    }
514
515    from_float! {
516        f32 =>                                i128;
517        f64 =>                                i128,                           u128;
518    }
519}
520
521// The missing piece
522impl From<f64> for f32 {
523    type Output = Result<f32, Error>;
524
525    #[inline]
526    fn cast(src: f64) -> Self::Output {
527        use core::{f32, f64};
528
529        if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY {
530            Ok(src as f32)
531        } else if src < f32::MIN as f64 {
532            Err(Error::Underflow)
533        } else if src > f32::MAX as f64 {
534            Err(Error::Overflow)
535        } else {
536            Ok(src as f32)
537        }
538    }
539}