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}