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