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