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 ] |
99 | extern crate quickcheck; |
100 | |
101 | use core::fmt; |
102 | #[cfg (feature = "std" )] |
103 | use std::error; |
104 | |
105 | #[cfg (test)] |
106 | mod test; |
107 | |
108 | /// Cast errors |
109 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
110 | pub 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 | |
123 | impl 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 | |
136 | impl 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" )] |
143 | impl error::Error for Error { |
144 | fn description(&self) -> &str { |
145 | self.description_helper() |
146 | } |
147 | } |
148 | |
149 | /// The "cast from" operation |
150 | pub 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 | |
158 | macro_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 | |
172 | fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); |
173 | |
174 | fns!(i128, u128); |
175 | |
176 | /// `$dst` can hold any value of `$src` |
177 | macro_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` |
195 | macro_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` |
217 | macro_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` |
241 | macro_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` |
267 | macro_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 |
323 | macro_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" )] |
355 | mod _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" )] |
413 | mod _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 | |
470 | mod _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 |
522 | impl 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 | |