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}
540