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//! ## Building without `std`
85//!
86//! This crate can be used without Rust's `std` crate by declaring it as
87//! follows in your `Cargo.toml`:
88//!
89//! ``` toml
90//! cast = { version = "*", default-features = false }
91//! ```
92
93#![deny(missing_docs)]
94#![deny(warnings)]
95#![allow(const_err)]
96
97#![cfg_attr(not(feature = "std"), no_std)]
98
99#![cfg_attr(feature = "x128", feature(i128_type, i128))]
100
101#[cfg(feature = "std")]
102extern crate core;
103
104#[cfg(test)]
105#[macro_use]
106extern crate quickcheck;
107
108use core::fmt;
109#[cfg(feature="std")]
110use std::error;
111
112#[cfg(test)]
113mod test;
114
115/// Cast errors
116#[derive(Clone, Copy, Debug, Eq, PartialEq)]
117pub enum Error {
118 /// Infinite value casted to a type that can only represent finite values
119 Infinite,
120 /// NaN value casted to a type that can't represent a NaN value
121 NaN,
122 /// Source value is greater than the maximum value that the destination type
123 /// can hold
124 Overflow,
125 /// Source value is smaller than the minimum value that the destination type
126 /// can hold
127 Underflow,
128}
129
130impl Error {
131 /// A private helper function that implements `description`, because
132 /// `description` is only available when we have `std` enabled.
133 fn description_helper(&self) -> &str {
134 match *self {
135 Error::Infinite => "Cannot store infinite value in finite type",
136 Error::NaN => "Cannot store NaN in type which does not support it",
137 Error::Overflow => "Overflow during numeric conversion",
138 Error::Underflow => "Underflow during numeric conversion",
139 }
140 }
141}
142
143impl fmt::Display for Error {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 write!(f, "{}", self.description_helper())
146 }
147}
148
149#[cfg(feature="std")]
150impl error::Error for Error {
151 fn description(&self) -> &str {
152 self.description_helper()
153 }
154}
155
156/// The "cast from" operation
157pub trait From<Src> {
158 /// The result of the cast operation: either `Self` or `Result<Self, Error>`
159 type Output;
160
161 /// Checked cast from `Src` to `Self`
162 fn cast(Src) -> Self::Output;
163}
164
165macro_rules! fns {
166 ($($ty:ident),+) => {
167 $(
168 /// Checked cast function
169 #[inline]
170 pub fn $ty<T>(x: T) -> <$ty as From<T>>::Output
171 where $ty: From<T>
172 {
173 <$ty as From<T>>::cast(x)
174 }
175 )+
176 }
177}
178
179fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
180
181#[cfg(feature = "x128")]
182fns!(i128, u128);
183
184/// `$dst` can hold any value of `$src`
185macro_rules! promotion {
186 ($($src:ty => $($dst: ty),+);+;) => {
187 $(
188 $(
189 impl From<$src> for $dst {
190 type Output = $dst;
191
192 #[inline]
193 fn cast(src: $src) -> $dst {
194 src as $dst
195 }
196 }
197 )+
198 )+
199 }
200}
201
202/// `$dst` can hold any positive value of `$src`
203macro_rules! half_promotion {
204 ($($src:ty => $($dst:ty),+);+;) => {
205 $(
206 $(
207 impl From<$src> for $dst {
208 type Output = Result<$dst, Error>;
209
210 #[inline]
211 fn cast(src: $src) -> Self::Output {
212 if src < 0 {
213 Err(Error::Underflow)
214 } else {
215 Ok(src as $dst)
216 }
217 }
218 }
219 )+
220 )+
221 }
222}
223
224/// From an unsigned `$src` to a smaller `$dst`
225macro_rules! from_unsigned {
226 ($($src:ident => $($dst:ident),+);+;) => {
227 $(
228 $(
229 impl From<$src> for $dst {
230 type Output = Result<$dst, Error>;
231
232 #[inline]
233 fn cast(src: $src) -> Self::Output {
234 use core::$dst;
235
236 if src > $dst::MAX as $src {
237 Err(Error::Overflow)
238 } else {
239 Ok(src as $dst)
240 }
241 }
242 }
243 )+
244 )+
245 }
246}
247
248/// From a signed `$src` to a smaller `$dst`
249macro_rules! from_signed {
250 ($($src:ident => $($dst:ident),+);+;) => {
251 $(
252 $(
253 impl From<$src> for $dst {
254 type Output = Result<$dst, Error>;
255
256 #[inline]
257 fn cast(src: $src) -> Self::Output {
258 use core::$dst;
259
260 Err(if src < $dst::MIN as $src {
261 Error::Underflow
262 } else if src > $dst::MAX as $src {
263 Error::Overflow
264 } else {
265 return Ok(src as $dst);
266 })
267 }
268 }
269 )+
270 )+
271 }
272}
273
274/// From a float `$src` to an integer `$dst`
275macro_rules! from_float {
276 ($($src:ident => $($dst:ident),+);+;) => {
277 $(
278 $(
279 impl From<$src> for $dst {
280 type Output = Result<$dst, Error>;
281
282 #[inline]
283 fn cast(src: $src) -> Self::Output {
284 use core::{$dst, $src};
285
286 Err(if src != src {
287 Error::NaN
288 } else if src == $src::INFINITY ||
289 src == $src::NEG_INFINITY {
290 Error::Infinite
291 } else if src < $dst::MIN as $src {
292 Error::Underflow
293 } else if src > $dst::MAX as $src {
294 Error::Overflow
295 } else {
296 return Ok(src as $dst);
297 })
298 }
299 }
300 )+
301 )+
302 }
303}
304
305/// From a float `$src` to an integer `$dst`, where $dst is large enough to contain
306/// all values of `$src`. We can't ever overflow here
307macro_rules! from_float_dst {
308 ($($src:ident => $($dst:ident),+);+;) => {
309 $(
310 $(
311 impl From<$src> for $dst {
312 type Output = Result<$dst, Error>;
313
314 #[inline]
315 #[allow(unused_comparisons)]
316 fn cast(src: $src) -> Self::Output {
317 use core::{$dst, $src};
318
319 Err(if src != src {
320 Error::NaN
321 } else if src == $src::INFINITY ||
322 src == $src::NEG_INFINITY {
323 Error::Infinite
324 } else if ($dst::MIN == 0) && src < 0.0 {
325 Error::Underflow
326 } else {
327 return Ok(src as $dst);
328 })
329 }
330 }
331 )+
332 )+
333 }
334}
335
336// PLAY TETRIS! ;-)
337
338#[cfg(target_pointer_width = "32")]
339mod _32 {
340 use {Error, From};
341
342 // Signed
343 promotion! {
344 i8 => f32, f64, i8, i16, i32, isize, i64;
345 i16 => f32, f64, i16, i32, isize, i64;
346 i32 => f32, f64, i32, isize, i64;
347 isize => f32, f64, i32, isize, i64;
348 i64 => f32, f64, i64;
349 }
350
351 half_promotion! {
352 i8 => u8, u16, u32, usize, u64;
353 i16 => u16, u32, usize, u64;
354 i32 => u32, usize, u64;
355 isize => u32, usize, u64;
356 i64 => u64;
357 }
358
359 from_signed! {
360
361 i16 => i8, u8;
362 i32 => i8, i16, u8, u16;
363 isize => i8, i16, u8, u16;
364 i64 => i8, i16, i32, isize, u8, u16, u32, usize;
365 }
366
367 // Unsigned
368 promotion! {
369 u8 => f32, f64, i16, i32, isize, i64, u8, u16, u32, usize, u64;
370 u16 => f32, f64, i32, isize, i64, u16, u32, usize, u64;
371 u32 => f32, f64, i64, u32, usize, u64;
372 usize => f32, f64, i64, u32, usize, u64;
373 u64 => f32, f64, u64;
374 }
375
376 from_unsigned! {
377 u8 => i8;
378 u16 => i8, i16, u8;
379 u32 => i8, i16, i32, isize, u8, u16;
380 usize => i8, i16, i32, isize, u8, u16;
381 u64 => i8, i16, i32, isize, i64, u8, u16, u32, usize;
382 }
383
384 // Float
385 promotion! {
386 f32 => f32, f64;
387 f64 => f64;
388 }
389
390 from_float! {
391 f32 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
392 f64 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
393 }
394}
395
396#[cfg(target_pointer_width = "64")]
397mod _64 {
398 use {Error, From};
399
400 // Signed
401 promotion! {
402 i8 => f32, f64, i8, i16, i32, i64, isize;
403 i16 => f32, f64, i16, i32, i64, isize;
404 i32 => f32, f64, i32, i64, isize;
405 i64 => f32, f64, i64, isize;
406 isize => f32, f64, i64, isize;
407 }
408
409 half_promotion! {
410 i8 => u8, u16, u32, u64, usize;
411 i16 => u16, u32, u64, usize;
412 i32 => u32, u64, usize;
413 i64 => u64, usize;
414 isize => u64, usize;
415 }
416
417 from_signed! {
418
419 i16 => i8, u8;
420 i32 => i8, i16, u8, u16;
421 i64 => i8, i16, i32, u8, u16, u32;
422 isize => i8, i16, i32, u8, u16, u32;
423 }
424
425 // Unsigned
426 promotion! {
427 u8 => f32, f64, i16, i32, i64, isize, u8, u16, u32, u64, usize;
428 u16 => f32, f64, i32, i64, isize, u16, u32, u64, usize;
429 u32 => f32, f64, i64, isize, u32, u64, usize;
430 u64 => f32, f64, u64, usize;
431 usize => f32, f64, u64, usize;
432 }
433
434 from_unsigned! {
435 u8 => i8;
436 u16 => i8, i16, u8;
437 u32 => i8, i16, i32, u8, u16;
438 u64 => i8, i16, i32, i64, isize, u8, u16, u32;
439 usize => i8, i16, i32, i64, isize, u8, u16, u32;
440 }
441
442 // Float
443 promotion! {
444 f32 => f32, f64;
445 f64 => f64;
446 }
447
448 from_float! {
449 f32 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
450 f64 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
451 }
452}
453
454#[cfg(feature = "x128")]
455mod _x128 {
456 use {Error, From};
457
458 // Signed
459 promotion! {
460 i8 => i128;
461 i16 => i128;
462 i32 => i128;
463 i64 => i128;
464 isize => i128;
465 i128 => f32, f64, i128;
466 }
467
468 half_promotion! {
469 i8 => u128;
470 i16 => u128;
471 i32 => u128;
472 i64 => u128;
473 isize => u128;
474 i128 => u128;
475 }
476
477 from_signed! {
478 i128 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
479 }
480
481 // Unsigned
482 promotion! {
483 u8 => i128, u128;
484 u16 => i128, u128;
485 u32 => i128, u128;
486 u64 => i128, u128;
487 usize => i128, u128;
488 u128 => f64, u128;
489 }
490
491 from_unsigned! {
492 u128 => f32, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, usize;
493 }
494
495 // Float
496 from_float! {
497 f32 => i128;
498 f64 => i128, u128;
499 }
500 from_float_dst! {
501 f32 => u128;
502 }
503}
504
505// The missing piece
506impl From<f64> for f32 {
507 type Output = Result<f32, Error>;
508
509 #[inline]
510 fn cast(src: f64) -> Self::Output {
511 use core::{f32, f64};
512
513 if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY {
514 Ok(src as f32)
515 } else if src < f32::MIN as f64 {
516 Err(Error::Underflow)
517 } else if src > f32::MAX as f64 {
518 Err(Error::Overflow)
519 } else {
520 Ok(src as f32)
521 }
522 }
523}