blob: 4a8efe9bd7a2884c7818a6572ec29eba9e62e9e0 [file] [log] [blame]
//! Ergonomic, checked cast functions for primitive types
//!
//! This crate provides one checked cast function for each numeric primitive.
//! Use these functions to perform a cast from any other numeric primitive:
//!
//! ```
//! extern crate cast;
//!
//! use cast::{u8, u16, Error};
//!
//! # fn main() {
//! // Infallible operations, like integer promotion, are equivalent to a normal
//! // cast with `as`
//! assert_eq!(u16(0u8), 0u16);
//!
//! // Everything else will return a `Result` depending on the success of the
//! // operation
//! assert_eq!(u8(0u16), Ok(0u8));
//! assert_eq!(u8(256u16), Err(Error::Overflow));
//! assert_eq!(u8(-1i8), Err(Error::Underflow));
//! assert_eq!(u8(1. / 0.), Err(Error::Infinite));
//! assert_eq!(u8(0. / 0.), Err(Error::NaN));
//! # }
//! ```
//!
//! There are no namespace problems between these functions, the "primitive
//! modules" in `core`/`std` and the built-in primitive types, so all them can
//! be in the same scope:
//!
//! ```
//! extern crate cast;
//!
//! use std::u8;
//! use cast::{u8, u16};
//!
//! # fn main() {
//! // `u8` as a type
//! let x: u8 = 0;
//! // `u8` as a module
//! let y = u16(u8::MAX);
//! // `u8` as a function
//! let z = u8(y).unwrap();
//! # }
//! ```
//!
//! The checked cast functionality is also usable with type aliases via the
//! `cast` static method:
//!
//! ```
//! extern crate cast;
//!
//! use std::os::raw::c_ulonglong;
//! // NOTE avoid shadowing `std::convert::From` - cf. rust-lang/rfcs#1311
//! use cast::From as _0;
//!
//! # fn main() {
//! assert_eq!(c_ulonglong::cast(0u8), 0u64);
//! # }
//! ```
//!
//! This crate also provides a `From` trait that can be used, for example,
//! to create a generic function that accepts any type that can be infallibly
//! casted to `u32`.
//!
//! ```
//! extern crate cast;
//!
//! fn to_u32<T>(x: T) -> u32
//! // reads as: "where u32 can be casted from T with output u32"
//! where u32: cast::From<T, Output=u32>,
//! {
//! cast::u32(x)
//! }
//!
//! # fn main() {
//! assert_eq!(to_u32(0u8), 0u32);
//! assert_eq!(to_u32(1u16), 1u32);
//! assert_eq!(to_u32(2u32), 2u32);
//!
//! // to_u32(-1i32); // Compile error
//! # }
//! ```
//!
//! ## Minimal Supported Rust Version
//!
//! This crate is guaranteed to compile on stable Rust 1.13 and up. It *might* compile on older
//! versions but that may change in any new patch release.
//!
//! ## Building without `std`
//!
//! This crate can be used without Rust's `std` crate by declaring it as
//! follows in your `Cargo.toml`:
//!
//! ``` toml
//! cast = { version = "*", default-features = false }
//! ```
#![deny(missing_docs)]
#![deny(warnings)]
#![allow(const_err)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(all(feature = "x128", not(stable_i128)), feature(i128_type, i128))]
#[cfg(feature = "std")]
extern crate core;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
use core::fmt;
#[cfg(feature = "std")]
use std::error;
#[cfg(test)]
mod test;
/// Cast errors
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Error {
/// Infinite value casted to a type that can only represent finite values
Infinite,
/// NaN value casted to a type that can't represent a NaN value
NaN,
/// Source value is greater than the maximum value that the destination type
/// can hold
Overflow,
/// Source value is smaller than the minimum value that the destination type
/// can hold
Underflow,
}
impl Error {
/// A private helper function that implements `description`, because
/// `description` is only available when we have `std` enabled.
fn description_helper(&self) -> &str {
match *self {
Error::Infinite => "Cannot store infinite value in finite type",
Error::NaN => "Cannot store NaN in type which does not support it",
Error::Overflow => "Overflow during numeric conversion",
Error::Underflow => "Underflow during numeric conversion",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description_helper())
}
}
#[cfg(feature = "std")]
impl error::Error for Error {
fn description(&self) -> &str {
self.description_helper()
}
}
/// The "cast from" operation
pub trait From<Src> {
/// The result of the cast operation: either `Self` or `Result<Self, Error>`
type Output;
/// Checked cast from `Src` to `Self`
fn cast(Src) -> Self::Output;
}
macro_rules! fns {
($($ty:ident),+) => {
$(
/// Checked cast function
#[inline]
pub fn $ty<T>(x: T) -> <$ty as From<T>>::Output
where $ty: From<T>
{
<$ty as From<T>>::cast(x)
}
)+
}
}
fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(feature = "x128")]
fns!(i128, u128);
/// `$dst` can hold any value of `$src`
macro_rules! promotion {
($($src:ty => $($dst: ty),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = $dst;
#[inline]
fn cast(src: $src) -> $dst {
src as $dst
}
}
)+
)+
}
}
/// `$dst` can hold any positive value of `$src`
macro_rules! half_promotion {
($($src:ty => $($dst:ty),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
if src < 0 {
Err(Error::Underflow)
} else {
Ok(src as $dst)
}
}
}
)+
)+
}
}
/// From an unsigned `$src` to a smaller `$dst`
macro_rules! from_unsigned {
($($src:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
use core::$dst;
if src > $dst::MAX as $src {
Err(Error::Overflow)
} else {
Ok(src as $dst)
}
}
}
)+
)+
}
}
/// From a signed `$src` to a smaller `$dst`
macro_rules! from_signed {
($($src:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
use core::$dst;
Err(if src < $dst::MIN as $src {
Error::Underflow
} else if src > $dst::MAX as $src {
Error::Overflow
} else {
return Ok(src as $dst);
})
}
}
)+
)+
}
}
/// From a float `$src` to an integer `$dst`
macro_rules! from_float {
($($src:ident, $usrc:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
fn cast(src: $src) -> Self::Output {
use core::{$dst, $src};
Err(if src != src {
Error::NaN
} else if src == $src::INFINITY ||
src == $src::NEG_INFINITY {
Error::Infinite
} else if {
// we subtract 1 ULP (unit of least precision) here because some
// lossy conversions like `u64::MAX as f64` round *up* and we want
// to avoid this evaluating to false in that case
use core::mem::transmute;
let max = unsafe {
transmute::<_, $src>(transmute::<_, $usrc>($dst::MAX as $src) - 1)
};
src > max
} {
Error::Overflow
} else if $dst::MIN == 0 {
// when casting to unsigned integer, negative values close to 0 but
// larger than 1.0 should be truncated to 0; this behavior matches
// casting from a float to a signed integer
if src <= -1.0 {
Error::Underflow
} else {
return Ok(src as $dst);
}
} else if src < $dst::MIN as $src {
Error::Underflow
} else {
return Ok(src as $dst);
})
}
}
)+
)+
}
}
/// From a float `$src` to an integer `$dst`, where $dst is large enough to contain
/// all values of `$src`. We can't ever overflow here
#[cfg(feature = "x128")]
macro_rules! from_float_dst {
($($src:ident => $($dst:ident),+);+;) => {
$(
$(
impl From<$src> for $dst {
type Output = Result<$dst, Error>;
#[inline]
#[allow(unused_comparisons)]
fn cast(src: $src) -> Self::Output {
use core::{$dst, $src};
Err(if src != src {
Error::NaN
} else if src == $src::INFINITY ||
src == $src::NEG_INFINITY {
Error::Infinite
} else if ($dst::MIN == 0) && src <= -1.0 {
Error::Underflow
} else {
return Ok(src as $dst);
})
}
}
)+
)+
}
}
// PLAY TETRIS! ;-)
#[cfg(target_pointer_width = "32")]
mod _32 {
use {Error, From};
// Signed
promotion! {
i8 => f32, f64, i8, i16, i32, isize, i64;
i16 => f32, f64, i16, i32, isize, i64;
i32 => f32, f64, i32, isize, i64;
isize => f32, f64, i32, isize, i64;
i64 => f32, f64, i64;
}
half_promotion! {
i8 => u8, u16, u32, usize, u64;
i16 => u16, u32, usize, u64;
i32 => u32, usize, u64;
isize => u32, usize, u64;
i64 => u64;
}
from_signed! {
i16 => i8, u8;
i32 => i8, i16, u8, u16;
isize => i8, i16, u8, u16;
i64 => i8, i16, i32, isize, u8, u16, u32, usize;
}
// Unsigned
promotion! {
u8 => f32, f64, i16, i32, isize, i64, u8, u16, u32, usize, u64;
u16 => f32, f64, i32, isize, i64, u16, u32, usize, u64;
u32 => f32, f64, i64, u32, usize, u64;
usize => f32, f64, i64, u32, usize, u64;
u64 => f32, f64, u64;
}
from_unsigned! {
u8 => i8;
u16 => i8, i16, u8;
u32 => i8, i16, i32, isize, u8, u16;
usize => i8, i16, i32, isize, u8, u16;
u64 => i8, i16, i32, isize, i64, u8, u16, u32, usize;
}
// Float
promotion! {
f32 => f32, f64;
f64 => f64;
}
from_float! {
f32, u32 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
f64, u64 => i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
}
}
#[cfg(target_pointer_width = "64")]
mod _64 {
use {Error, From};
// Signed
promotion! {
i8 => f32, f64, i8, i16, i32, i64, isize;
i16 => f32, f64, i16, i32, i64, isize;
i32 => f32, f64, i32, i64, isize;
i64 => f32, f64, i64, isize;
isize => f32, f64, i64, isize;
}
half_promotion! {
i8 => u8, u16, u32, u64, usize;
i16 => u16, u32, u64, usize;
i32 => u32, u64, usize;
i64 => u64, usize;
isize => u64, usize;
}
from_signed! {
i16 => i8, u8;
i32 => i8, i16, u8, u16;
i64 => i8, i16, i32, u8, u16, u32;
isize => i8, i16, i32, u8, u16, u32;
}
// Unsigned
promotion! {
u8 => f32, f64, i16, i32, i64, isize, u8, u16, u32, u64, usize;
u16 => f32, f64, i32, i64, isize, u16, u32, u64, usize;
u32 => f32, f64, i64, isize, u32, u64, usize;
u64 => f32, f64, u64, usize;
usize => f32, f64, u64, usize;
}
from_unsigned! {
u8 => i8;
u16 => i8, i16, u8;
u32 => i8, i16, i32, u8, u16;
u64 => i8, i16, i32, i64, isize, u8, u16, u32;
usize => i8, i16, i32, i64, isize, u8, u16, u32;
}
// Float
promotion! {
f32 => f32, f64;
f64 => f64;
}
from_float! {
f32, u32 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
f64, u64 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
}
}
#[cfg(feature = "x128")]
mod _x128 {
use {Error, From};
// Signed
promotion! {
i8 => i128;
i16 => i128;
i32 => i128;
i64 => i128;
isize => i128;
i128 => f32, f64, i128;
}
half_promotion! {
i8 => u128;
i16 => u128;
i32 => u128;
i64 => u128;
isize => u128;
i128 => u128;
}
from_signed! {
i128 => i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
}
// Unsigned
promotion! {
u8 => i128, u128;
u16 => i128, u128;
u32 => i128, u128;
u64 => i128, u128;
usize => i128, u128;
u128 => f64, u128;
}
from_unsigned! {
u128 => f32, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, usize;
}
// Float
from_float! {
f32, u32 => i128;
f64, u64 => i128, u128;
}
from_float_dst! {
f32 => u128;
}
}
// The missing piece
impl From<f64> for f32 {
type Output = Result<f32, Error>;
#[inline]
fn cast(src: f64) -> Self::Output {
use core::{f32, f64};
if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY {
Ok(src as f32)
} else if src < f32::MIN as f64 {
Err(Error::Underflow)
} else if src > f32::MAX as f64 {
Err(Error::Overflow)
} else {
Ok(src as f32)
}
}
}