Add safe cast operation traits
This commit adds safe cast operation traits which each have only one of the properties from a naked cast. This allows the clear definition of specific properties in a generic context. All methods defined in this commit are zero-cost abstractions.
This commit is contained in:
parent
5c24fcc4a7
commit
955c45b933
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod safe;
|
||||||
|
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use core::num::Wrapping;
|
use core::num::Wrapping;
|
||||||
use core::{f32, f64};
|
use core::{f32, f64};
|
|
@ -0,0 +1,103 @@
|
||||||
|
/// Cast from one numeric type to another.
|
||||||
|
///
|
||||||
|
/// The `CastFrom<T>` trait is similar to `std::convert::From<T>`. However,
|
||||||
|
/// while `std::convert::From<T>` performs a logical conversion, `CastFrom<T>`
|
||||||
|
/// performs a bitwise cast from one number to another; possibly of a different
|
||||||
|
/// size, signedness or even numeric type.
|
||||||
|
///
|
||||||
|
/// Unless you really know what you are doing, you probably don't want this
|
||||||
|
/// trait. Instead, you should check out the following traits:
|
||||||
|
///
|
||||||
|
/// * `GrowFrom<T>`
|
||||||
|
/// * `TrimFrom<T>`
|
||||||
|
/// * `SignCast`
|
||||||
|
pub trait CastFrom<T> {
|
||||||
|
fn cast(value: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast from one numeric type to another.
|
||||||
|
///
|
||||||
|
/// The `CastInto<T>` trait is similar to `std::convert::Into<T>`. However,
|
||||||
|
/// while `std::convert::Into<T>` performs a logical conversion, `CastInto<T>`
|
||||||
|
/// performs a bitwise cast from one number to another; possibly of a different
|
||||||
|
/// size, signedness or even numeric type.
|
||||||
|
///
|
||||||
|
/// Unless you really know what you are doing, you probably don't want this
|
||||||
|
/// trait. Instead, you should check out the following traits:
|
||||||
|
///
|
||||||
|
/// * `GrowInto<T>`
|
||||||
|
/// * `TrimInto<T>`
|
||||||
|
/// * `SignCast`
|
||||||
|
pub trait CastInto<T> {
|
||||||
|
fn cast(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CastFrom implies CastInto
|
||||||
|
impl<T, U> CastInto<U> for T where U: CastFrom<T>
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn cast(self) -> U {
|
||||||
|
U::cast(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CastFrom (and thus CastInto) is reflexive
|
||||||
|
impl<T> CastFrom<T> for T {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn cast(t: T) -> T { t }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cast_impl {
|
||||||
|
($from:ty > $into:ty) => (
|
||||||
|
impl CastFrom<$from> for $into {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn cast(value: $from) -> $into {
|
||||||
|
value as $into
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
(i128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
cast_impl! { i128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
(u128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
cast_impl! { u128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => i128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
cast_impl! { $from > i128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => u128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
cast_impl! { $from > u128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => $into:ty) => (
|
||||||
|
cast_impl! { $from > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($kind:ty, $($next:ty),+) => (
|
||||||
|
$(
|
||||||
|
cast_impl! { $kind => $next }
|
||||||
|
cast_impl! { $next => $kind }
|
||||||
|
)+
|
||||||
|
|
||||||
|
cast_impl! { $($next),+ }
|
||||||
|
);
|
||||||
|
|
||||||
|
($kind:ty) => ();
|
||||||
|
}
|
||||||
|
|
||||||
|
cast_impl! {
|
||||||
|
usize, u128, u64, u32, u16, u8,
|
||||||
|
isize, i128, i64, i32, i16, i8,
|
||||||
|
f32, f64
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/// Cast from a smaller number to a larger one without changing sign.
|
||||||
|
///
|
||||||
|
/// The `GrowFrom<T>` trait is similar to `std::convert::From<T>`. However,
|
||||||
|
/// while `std::convert::From<T>` performs a logical conversion, `GrowFrom<T>`
|
||||||
|
/// performs a bitwise cast from a number to a number of the same sign but
|
||||||
|
/// of a possibly larger size. `GrowFrom<T>` will **never** decrease the size
|
||||||
|
/// of a number or change from an integer of one signedness to the other.
|
||||||
|
pub trait GrowFrom<T> {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn grow(value: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast from a smaller number to a larger one without changing sign.
|
||||||
|
///
|
||||||
|
/// The `GrowInto<T>` trait is similar to `std::convert::Into<T>`. However,
|
||||||
|
/// while `std::convert::Into<T>` performs a logical conversion, `GrowInto<T>`
|
||||||
|
/// performs a bitwise cast from a number to a number of the same sign but
|
||||||
|
/// of a possibly larger size. `GrowInto<T>` will **never** decrease the size
|
||||||
|
/// of a number or change from an integer of one signedness to the other.
|
||||||
|
pub trait GrowInto<T> {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn grow(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrowFrom implies GrowInto
|
||||||
|
impl<T, U> GrowInto<U> for T where U: GrowFrom<T>
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn grow(self) -> U {
|
||||||
|
U::grow(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrowFrom (and thus GrowInto) is reflexive
|
||||||
|
impl<T> GrowFrom<T> for T {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn grow(t: T) -> T { t }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! grow_impl {
|
||||||
|
($from:ty > $into:ty) => (
|
||||||
|
impl GrowFrom<$from> for $into {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn grow(value: $from) -> $into {
|
||||||
|
value as $into
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
(i128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
grow_impl! { i128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
(u128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
grow_impl! { u128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => i128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
grow_impl! { $from > i128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => u128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
grow_impl! { $from > u128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => $into:ty) => (
|
||||||
|
grow_impl! { $from > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($($from:ty : $($into:ty),+)+) => (
|
||||||
|
$( $( grow_impl! { $from => $into } )+ )+
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
grow_impl! {
|
||||||
|
u64: usize
|
||||||
|
i64: isize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
grow_impl! {
|
||||||
|
usize: u32
|
||||||
|
isize: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
grow_impl! {
|
||||||
|
usize: u128, u64
|
||||||
|
isize: i128, i64
|
||||||
|
|
||||||
|
u64: u128
|
||||||
|
i64: i128
|
||||||
|
|
||||||
|
u32: usize, u128, u64
|
||||||
|
i32: isize, i128, i64
|
||||||
|
|
||||||
|
u16: usize, u128, u64, u32
|
||||||
|
i16: isize, i128, i64, i32
|
||||||
|
|
||||||
|
u8: usize, u128, u64, u32, u16
|
||||||
|
i8: isize, i128, i64, i32, i16
|
||||||
|
|
||||||
|
f32: f64
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
//! Traits for safe casting in a generic context.
|
||||||
|
//!
|
||||||
|
//! Each of the traits herein has a varying degree of safety. Raw casting is
|
||||||
|
//! implicitly dangerous because it can perform four possible actions:
|
||||||
|
//! * Convert from integer to float or vice versa.
|
||||||
|
//! * Convert from signed to unsigned or vice versa.
|
||||||
|
//! * Increases the size (or precision) of a number without loss.
|
||||||
|
//! * Decreases the size (or precision) of a number with loss.
|
||||||
|
//!
|
||||||
|
//! By using the raw cast operation you may get unintentional side effects.
|
||||||
|
//! These will often compile without warning and appear only as bugs later.
|
||||||
|
//!
|
||||||
|
//! The purpose of this module is to provide zero-cost abstraction traits that
|
||||||
|
//! encapsulate each discrete action of a cast. When using the methods on these
|
||||||
|
//! traits, you will only ever get the intended explicit behavior.
|
||||||
|
//!
|
||||||
|
//! For example, here is a generic implementation of a trivial function which
|
||||||
|
//! takes any integer type that can hold at least 12 bits and returns the 12
|
||||||
|
//! least significant bits. Note that attempting to pass an 8-bit integer to
|
||||||
|
//! this function would result in a compiler error (since `TrimInto<u16>` is
|
||||||
|
//! not implemented for `u8`).
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use num_traits::cast::safe::{SignCast, TrimInto};
|
||||||
|
//!
|
||||||
|
//! const MASK: u16 = 0b0000111111111111;
|
||||||
|
//!
|
||||||
|
//! fn low12<T: TrimInto<u16>, C: SignCast<Unsigned=T>>(x: C) -> u16 {
|
||||||
|
//! x.unsigned().trim() & MASK
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! assert_eq!(low12(u64::max_value()), u64::max_value() as u16 & MASK);
|
||||||
|
//! assert_eq!(low12(-1i32), -1i32 as u16 & MASK);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! It is recommended that you use the most specific type of cast appropriate
|
||||||
|
//! for your own use. Using a more generalized cast results in reduced compile
|
||||||
|
//! time validation of your code.
|
||||||
|
//!
|
||||||
|
//! Recommended cast types are:
|
||||||
|
//!
|
||||||
|
//! * `GrowFrom<T>` / `GrowInto<T>`
|
||||||
|
//! * `TrimFrom<T>` / `TrimInto<T>`
|
||||||
|
//! * `SignCast`
|
||||||
|
|
||||||
|
mod cast;
|
||||||
|
mod grow;
|
||||||
|
mod sign;
|
||||||
|
mod trim;
|
||||||
|
mod size;
|
||||||
|
|
||||||
|
pub use self::cast::{CastFrom, CastInto};
|
||||||
|
pub use self::size::{SizeFrom, SizeInto};
|
||||||
|
pub use self::grow::{GrowFrom, GrowInto};
|
||||||
|
pub use self::trim::{TrimFrom, TrimInto};
|
||||||
|
pub use self::sign::SignCast;
|
|
@ -0,0 +1,232 @@
|
||||||
|
/// Casts an integer to a different sign without changing its size.
|
||||||
|
///
|
||||||
|
/// `SignCast` exposes the cast operator in generic contexts. It is used to
|
||||||
|
/// perform a signed cast between integers of the same size. The `SignCast`
|
||||||
|
/// trait will **never** change the size of the integer.
|
||||||
|
pub trait SignCast {
|
||||||
|
type Unsigned;
|
||||||
|
type Signed;
|
||||||
|
|
||||||
|
fn unsigned(self) -> Self::Unsigned;
|
||||||
|
fn signed(self) -> Self::Signed;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! sign_impl {
|
||||||
|
( $( $usig:ty : $sign:ty )+ ) => (
|
||||||
|
$(
|
||||||
|
impl SignCast for $usig {
|
||||||
|
type Unsigned = $usig;
|
||||||
|
type Signed = $sign;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn unsigned(self) -> $usig {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn signed(self) -> $sign {
|
||||||
|
self as $sign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignCast for $sign {
|
||||||
|
type Unsigned = $usig;
|
||||||
|
type Signed = $sign;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn unsigned(self) -> $usig {
|
||||||
|
self as $usig
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn signed(self) -> $sign {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
sign_impl! {
|
||||||
|
u128 : i128
|
||||||
|
}
|
||||||
|
|
||||||
|
sign_impl! {
|
||||||
|
usize : isize
|
||||||
|
u64 : i64
|
||||||
|
u32 : i32
|
||||||
|
u16 : i16
|
||||||
|
u8 : i8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::SignCast;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_size() {
|
||||||
|
// Signed to unsigned cast
|
||||||
|
assert_eq!(isize::min_value().unsigned(), isize::min_value() as usize);
|
||||||
|
assert_eq!(0isize.unsigned(), 0usize);
|
||||||
|
assert_eq!(isize::max_value().unsigned(), isize::max_value() as usize);
|
||||||
|
|
||||||
|
// Signed to signed cast
|
||||||
|
assert_eq!(isize::min_value().signed(), isize::min_value());
|
||||||
|
assert_eq!(0isize.signed(), 0isize);
|
||||||
|
assert_eq!(isize::max_value().signed(), isize::max_value());
|
||||||
|
|
||||||
|
// Unsigned to signed cast
|
||||||
|
assert_eq!(usize::min_value().signed(), usize::min_value() as isize);
|
||||||
|
assert_eq!(usize::max_value().signed(), usize::max_value() as isize);
|
||||||
|
|
||||||
|
// Unsigned to unsigned cast
|
||||||
|
assert_eq!(usize::min_value().unsigned(), usize::min_value());
|
||||||
|
assert_eq!(usize::max_value().unsigned(), usize::max_value());
|
||||||
|
|
||||||
|
// Test reciprocity
|
||||||
|
assert_eq!(isize::min_value().unsigned().signed(), isize::min_value());
|
||||||
|
assert_eq!(isize::max_value().unsigned().signed(), isize::max_value());
|
||||||
|
assert_eq!(usize::min_value().signed().unsigned(), usize::min_value());
|
||||||
|
assert_eq!(usize::max_value().signed().unsigned(), usize::max_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_128() {
|
||||||
|
// Signed to unsigned cast
|
||||||
|
assert_eq!(i128::min_value().unsigned(), i128::min_value() as u128);
|
||||||
|
assert_eq!(0i128.unsigned(), 0u128);
|
||||||
|
assert_eq!(i128::max_value().unsigned(), i128::max_value() as u128);
|
||||||
|
|
||||||
|
// Signed to signed cast
|
||||||
|
assert_eq!(i128::min_value().signed(), i128::min_value());
|
||||||
|
assert_eq!(0i128.signed(), 0i128);
|
||||||
|
assert_eq!(i128::max_value().signed(), i128::max_value());
|
||||||
|
|
||||||
|
// Unsigned to signed cast
|
||||||
|
assert_eq!(u128::min_value().signed(), u128::min_value() as i128);
|
||||||
|
assert_eq!(u128::max_value().signed(), u128::max_value() as i128);
|
||||||
|
|
||||||
|
// Unsigned to unsigned cast
|
||||||
|
assert_eq!(u128::min_value().unsigned(), u128::min_value());
|
||||||
|
assert_eq!(u128::max_value().unsigned(), u128::max_value());
|
||||||
|
|
||||||
|
// Test reciprocity
|
||||||
|
assert_eq!(i128::min_value().unsigned().signed(), i128::min_value());
|
||||||
|
assert_eq!(i128::max_value().unsigned().signed(), i128::max_value());
|
||||||
|
assert_eq!(u128::min_value().signed().unsigned(), u128::min_value());
|
||||||
|
assert_eq!(u128::max_value().signed().unsigned(), u128::max_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_64() {
|
||||||
|
// Signed to unsigned cast
|
||||||
|
assert_eq!(i64::min_value().unsigned(), i64::min_value() as u64);
|
||||||
|
assert_eq!(0i64.unsigned(), 0u64);
|
||||||
|
assert_eq!(i64::max_value().unsigned(), i64::max_value() as u64);
|
||||||
|
|
||||||
|
// Signed to signed cast
|
||||||
|
assert_eq!(i64::min_value().signed(), i64::min_value());
|
||||||
|
assert_eq!(0i64.signed(), 0i64);
|
||||||
|
assert_eq!(i64::max_value().signed(), i64::max_value());
|
||||||
|
|
||||||
|
// Unsigned to signed cast
|
||||||
|
assert_eq!(u64::min_value().signed(), u64::min_value() as i64);
|
||||||
|
assert_eq!(u64::max_value().signed(), u64::max_value() as i64);
|
||||||
|
|
||||||
|
// Unsigned to unsigned cast
|
||||||
|
assert_eq!(u64::min_value().unsigned(), u64::min_value());
|
||||||
|
assert_eq!(u64::max_value().unsigned(), u64::max_value());
|
||||||
|
|
||||||
|
// Test reciprocity
|
||||||
|
assert_eq!(i64::min_value().unsigned().signed(), i64::min_value());
|
||||||
|
assert_eq!(i64::max_value().unsigned().signed(), i64::max_value());
|
||||||
|
assert_eq!(u64::min_value().signed().unsigned(), u64::min_value());
|
||||||
|
assert_eq!(u64::max_value().signed().unsigned(), u64::max_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_32() {
|
||||||
|
// Signed to unsigned cast
|
||||||
|
assert_eq!(i32::min_value().unsigned(), i32::min_value() as u32);
|
||||||
|
assert_eq!(0i32.unsigned(), 0u32);
|
||||||
|
assert_eq!(i32::max_value().unsigned(), i32::max_value() as u32);
|
||||||
|
|
||||||
|
// Signed to signed cast
|
||||||
|
assert_eq!(i32::min_value().signed(), i32::min_value());
|
||||||
|
assert_eq!(0i32.signed(), 0i32);
|
||||||
|
assert_eq!(i32::max_value().signed(), i32::max_value());
|
||||||
|
|
||||||
|
// Unsigned to signed cast
|
||||||
|
assert_eq!(u32::min_value().signed(), u32::min_value() as i32);
|
||||||
|
assert_eq!(u32::max_value().signed(), u32::max_value() as i32);
|
||||||
|
|
||||||
|
// Unsigned to unsigned cast
|
||||||
|
assert_eq!(u32::min_value().unsigned(), u32::min_value());
|
||||||
|
assert_eq!(u32::max_value().unsigned(), u32::max_value());
|
||||||
|
|
||||||
|
// Test reciprocity
|
||||||
|
assert_eq!(i32::min_value().unsigned().signed(), i32::min_value());
|
||||||
|
assert_eq!(i32::max_value().unsigned().signed(), i32::max_value());
|
||||||
|
assert_eq!(u32::min_value().signed().unsigned(), u32::min_value());
|
||||||
|
assert_eq!(u32::max_value().signed().unsigned(), u32::max_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_16() {
|
||||||
|
// Signed to unsigned cast
|
||||||
|
assert_eq!(i16::min_value().unsigned(), i16::min_value() as u16);
|
||||||
|
assert_eq!(0i16.unsigned(), 0u16);
|
||||||
|
assert_eq!(i16::max_value().unsigned(), i16::max_value() as u16);
|
||||||
|
|
||||||
|
// Signed to signed cast
|
||||||
|
assert_eq!(i16::min_value().signed(), i16::min_value());
|
||||||
|
assert_eq!(0i16.signed(), 0i16);
|
||||||
|
assert_eq!(i16::max_value().signed(), i16::max_value());
|
||||||
|
|
||||||
|
// Unsigned to signed cast
|
||||||
|
assert_eq!(u16::min_value().signed(), u16::min_value() as i16);
|
||||||
|
assert_eq!(u16::max_value().signed(), u16::max_value() as i16);
|
||||||
|
|
||||||
|
// Unsigned to unsigned cast
|
||||||
|
assert_eq!(u16::min_value().unsigned(), u16::min_value());
|
||||||
|
assert_eq!(u16::max_value().unsigned(), u16::max_value());
|
||||||
|
|
||||||
|
// Test reciprocity
|
||||||
|
assert_eq!(i16::min_value().unsigned().signed(), i16::min_value());
|
||||||
|
assert_eq!(i16::max_value().unsigned().signed(), i16::max_value());
|
||||||
|
assert_eq!(u16::min_value().signed().unsigned(), u16::min_value());
|
||||||
|
assert_eq!(u16::max_value().signed().unsigned(), u16::max_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_8() {
|
||||||
|
// Signed to unsigned cast
|
||||||
|
assert_eq!(i8::min_value().unsigned(), i8::min_value() as u8);
|
||||||
|
assert_eq!(0i8.unsigned(), 0u8);
|
||||||
|
assert_eq!(i8::max_value().unsigned(), i8::max_value() as u8);
|
||||||
|
|
||||||
|
// Signed to signed cast
|
||||||
|
assert_eq!(i8::min_value().signed(), i8::min_value());
|
||||||
|
assert_eq!(0i8.signed(), 0i8);
|
||||||
|
assert_eq!(i8::max_value().signed(), i8::max_value());
|
||||||
|
|
||||||
|
// Unsigned to signed cast
|
||||||
|
assert_eq!(u8::min_value().signed(), u8::min_value() as i8);
|
||||||
|
assert_eq!(u8::max_value().signed(), u8::max_value() as i8);
|
||||||
|
|
||||||
|
// Unsigned to unsigned cast
|
||||||
|
assert_eq!(u8::min_value().unsigned(), u8::min_value());
|
||||||
|
assert_eq!(u8::max_value().unsigned(), u8::max_value());
|
||||||
|
|
||||||
|
// Test reciprocity
|
||||||
|
assert_eq!(i8::min_value().unsigned().signed(), i8::min_value());
|
||||||
|
assert_eq!(i8::max_value().unsigned().signed(), i8::max_value());
|
||||||
|
assert_eq!(u8::min_value().signed().unsigned(), u8::min_value());
|
||||||
|
assert_eq!(u8::max_value().signed().unsigned(), u8::max_value());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/// Cast between different sized numbers without changing sign.
|
||||||
|
///
|
||||||
|
/// The `SizeFrom<T>` trait is similar to `std::convert::From<T>`. However,
|
||||||
|
/// while `std::convert::From<T>` performs a logical conversion, `SizeFrom<T>`
|
||||||
|
/// performs a bitwise cast from a number to a number of the same sign but
|
||||||
|
/// of a possibly different size. `TrimFrom<T>` will **never** change from an
|
||||||
|
/// integer of one signedness to the other.
|
||||||
|
///
|
||||||
|
/// Unless you really know what you are doing, you probably don't want this
|
||||||
|
/// trait. Instead, you should check out the following traits:
|
||||||
|
///
|
||||||
|
/// * `GrowFrom<T>`
|
||||||
|
/// * `TrimFrom<T>`
|
||||||
|
/// * `SignCast`
|
||||||
|
pub trait SizeFrom<T> {
|
||||||
|
fn size(value: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast between different sized numbers without changing sign.
|
||||||
|
///
|
||||||
|
/// The `SizeInto<T>` trait is similar to `std::convert::Into<T>`. However,
|
||||||
|
/// while `std::convert::Into<T>` performs a logical conversion, `SizeInto<T>`
|
||||||
|
/// performs a bitwise cast from a number to a number of the same sign but
|
||||||
|
/// of a possibly different size. `TrimInto<T>` will **never** change from an
|
||||||
|
/// integer of one signedness to the other.
|
||||||
|
///
|
||||||
|
/// Unless you really know what you are doing, you probably don't want this
|
||||||
|
/// trait. Instead, you should check out the following traits:
|
||||||
|
///
|
||||||
|
/// * `GrowInto<T>`
|
||||||
|
/// * `TrimInto<T>`
|
||||||
|
/// * `SignCast`
|
||||||
|
pub trait SizeInto<T> {
|
||||||
|
fn size(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizeFrom implies SizeInto
|
||||||
|
impl<T, U> SizeInto<U> for T where U: SizeFrom<T>
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn size(self) -> U {
|
||||||
|
U::size(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizeFrom (and thus SizeInto) is reflexive
|
||||||
|
impl<T> SizeFrom<T> for T {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn size(t: T) -> T { t }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! size_impl {
|
||||||
|
($from:ty > $into:ty) => (
|
||||||
|
impl SizeFrom<$from> for $into {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn size(value: $from) -> $into {
|
||||||
|
value as $into
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
(i128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
size_impl! { i128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
(u128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
size_impl! { u128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => i128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
size_impl! { $from > i128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => u128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
size_impl! { $from > u128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => $into:ty) => (
|
||||||
|
size_impl! { $from > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($kind:ty, $($next:ty),+) => (
|
||||||
|
$(
|
||||||
|
size_impl! { $kind => $next }
|
||||||
|
size_impl! { $next => $kind }
|
||||||
|
)+
|
||||||
|
|
||||||
|
size_impl! { $($next),+ }
|
||||||
|
);
|
||||||
|
|
||||||
|
($kind:ty) => ();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_impl! { usize, u128, u64, u32, u16, u8 }
|
||||||
|
size_impl! { isize, i128, i64, i32, i16, i8 }
|
||||||
|
size_impl! { f32, f64 }
|
|
@ -0,0 +1,109 @@
|
||||||
|
/// Cast from a larger number to a smaller one without changing sign.
|
||||||
|
///
|
||||||
|
/// The `TrimFrom<T>` trait is similar to `std::convert::From<T>`. However,
|
||||||
|
/// while `std::convert::From<T>` performs a logical conversion, `TrimFrom<T>`
|
||||||
|
/// performs a bitwise cast from a number to a number of the same sign but
|
||||||
|
/// of a possibly smaller size. `TrimFrom<T>` will **never** increase the size
|
||||||
|
/// of a number or change from an integer of one signedness to the other.
|
||||||
|
pub trait TrimFrom<T> {
|
||||||
|
fn trim(value: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast from a larger number to a smaller one without changing sign.
|
||||||
|
///
|
||||||
|
/// The `TrimInto<T>` trait is similar to `std::convert::Into<T>`. However,
|
||||||
|
/// while `std::convert::Into<T>` performs a logical conversion, `TrimInto<T>`
|
||||||
|
/// performs a bitwise cast from a number to a number of the same sign but
|
||||||
|
/// of a possibly smaller size. `TrimInto<T>` will **never** increase the size
|
||||||
|
/// of a number or change from an integer of one signedness to the other.
|
||||||
|
pub trait TrimInto<T> {
|
||||||
|
fn trim(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimFrom implies TrimInto
|
||||||
|
impl<T, U> TrimInto<U> for T where U: TrimFrom<T>
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn trim(self) -> U {
|
||||||
|
U::trim(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimFrom (and thus TrimInto) is reflexive
|
||||||
|
impl<T> TrimFrom<T> for T {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn trim(t: T) -> T { t }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! trim_impl {
|
||||||
|
($from:ty > $into:ty) => (
|
||||||
|
impl TrimFrom<$from> for $into {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
fn trim(value: $from) -> $into {
|
||||||
|
value as $into
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
(i128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
trim_impl! { i128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
(u128 => $into:ty) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
trim_impl! { u128 > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => i128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
trim_impl! { $from > i128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => u128) => (
|
||||||
|
#[cfg(has_i128)]
|
||||||
|
trim_impl! { $from > u128 }
|
||||||
|
);
|
||||||
|
|
||||||
|
($from:ty => $into:ty) => (
|
||||||
|
trim_impl! { $from > $into }
|
||||||
|
);
|
||||||
|
|
||||||
|
($($from:ty : $($into:ty),+)+) => (
|
||||||
|
$( $( trim_impl! { $from => $into } )+ )+
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
trim_impl! {
|
||||||
|
usize: u64
|
||||||
|
isize: i64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
trim_impl! {
|
||||||
|
u32: usize
|
||||||
|
i32: isize
|
||||||
|
}
|
||||||
|
|
||||||
|
trim_impl! {
|
||||||
|
usize: u32, u16, u8
|
||||||
|
isize: i32, i16, i8
|
||||||
|
|
||||||
|
u128: usize, u64, u32, u16, u8
|
||||||
|
i128: isize, i64, i32, i16, i8
|
||||||
|
|
||||||
|
u64: usize, u32, u16, u8
|
||||||
|
i64: isize, i32, i16, i8
|
||||||
|
|
||||||
|
u32: u16, u8
|
||||||
|
i32: i16, i8
|
||||||
|
|
||||||
|
u16: u8
|
||||||
|
i16: i8
|
||||||
|
|
||||||
|
f64: f32
|
||||||
|
}
|
Loading…
Reference in New Issue