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::num::Wrapping;
|
||||
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