Skip to main content

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