17: Add AsPrimitive trait for generic casting with `as` r=cuviper a=Enet4 This is my personal attempt at #7. It is fairly similar to what can be found in `asprim`, although implemented from scratch. Please let me know of what you think. Could it use more tests? Should I also leave a safety notice that some conversions with `as` are currently UB (rust-lang/rust#10184)? 21: Add checked shifts r=cuviper a=fabianschuiki Add traits `CheckedShl` and `CheckedShr` that correspond to the standard library's `checked_shl` and `checked_shr` functions. Implement the trait on all primitive integer types by default, akin to what the standard library does. The stdlib is somewhat inconsistent when it comes to the type of the shift amount. The `checked_*` functions have a `u32` shift amount, but the `std::ops::{Shl,Shr}` traits are generic over the shift amount. Also the stdlib implements these traits for all primitive integer types as right-hand sides. Our implementation mimics this behaviour.
This commit is contained in:
commit
aa7c15e0e9
80
src/cast.rs
80
src/cast.rs
|
@ -452,6 +452,74 @@ impl<T: NumCast> NumCast for Wrapping<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A generic interface for casting between machine scalars with the
|
||||||
|
/// `as` operator, which admits narrowing and precision loss.
|
||||||
|
/// Implementers of this trait AsPrimitive should behave like a primitive
|
||||||
|
/// numeric type (e.g. a newtype around another primitive), and the
|
||||||
|
/// intended conversion must never fail.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use num_traits::AsPrimitive;
|
||||||
|
/// let three: i32 = (3.14159265f32).as_();
|
||||||
|
/// assert_eq!(three, 3);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Currently, some uses of the `as` operator are not entirely safe.
|
||||||
|
/// In particular, it is undefined behavior if:
|
||||||
|
///
|
||||||
|
/// - A truncated floating point value cannot fit in the target integer
|
||||||
|
/// type ([#10184](https://github.com/rust-lang/rust/issues/10184));
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// # use num_traits::AsPrimitive;
|
||||||
|
/// let x: u8 = (1.04E+17).as_(); // UB
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// - Or a floating point value does not fit in another floating
|
||||||
|
/// point type ([#15536](https://github.com/rust-lang/rust/issues/15536)).
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// # use num_traits::AsPrimitive;
|
||||||
|
/// let x: f32 = (1e300f64).as_(); // UB
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub trait AsPrimitive<T>: 'static + Copy
|
||||||
|
where
|
||||||
|
T: 'static + Copy
|
||||||
|
{
|
||||||
|
/// Convert a value to another, using the `as` operator.
|
||||||
|
fn as_(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_as_primitive {
|
||||||
|
($T: ty => $( $U: ty ),* ) => {
|
||||||
|
$(
|
||||||
|
impl AsPrimitive<$U> for $T {
|
||||||
|
#[inline] fn as_(self) -> $U { self as $U }
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_as_primitive!(u8 => char, u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(i8 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(u16 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(i16 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(u32 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(i32 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(u64 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(i64 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(usize => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(isize => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(f32 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(f64 => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64, f32, f64);
|
||||||
|
impl_as_primitive!(char => char, u8, i8, u16, i16, u32, i32, u64, isize, usize, i64);
|
||||||
|
impl_as_primitive!(bool => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_primitive_float() {
|
fn to_primitive_float() {
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
@ -509,3 +577,15 @@ fn wrapping_is_numcast() {
|
||||||
fn require_numcast<T: NumCast>(_: &T) {}
|
fn require_numcast<T: NumCast>(_: &T) {}
|
||||||
require_numcast(&Wrapping(42));
|
require_numcast(&Wrapping(42));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn as_primitive() {
|
||||||
|
let x: f32 = (1.625f64).as_();
|
||||||
|
assert_eq!(x, 1.625f32);
|
||||||
|
|
||||||
|
let x: f32 = (3.14159265358979323846f64).as_();
|
||||||
|
assert_eq!(x, 3.1415927f32);
|
||||||
|
|
||||||
|
let x: u8 = (768i16).as_();
|
||||||
|
assert_eq!(x, 0);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::ops::{Add, Sub, Mul, Div};
|
use std::ops::{Add, Sub, Mul, Div, Shl, Shr};
|
||||||
|
|
||||||
/// Performs addition that returns `None` instead of wrapping around on
|
/// Performs addition that returns `None` instead of wrapping around on
|
||||||
/// overflow.
|
/// overflow.
|
||||||
|
@ -90,3 +90,73 @@ checked_impl!(CheckedDiv, checked_div, i32);
|
||||||
checked_impl!(CheckedDiv, checked_div, i64);
|
checked_impl!(CheckedDiv, checked_div, i64);
|
||||||
checked_impl!(CheckedDiv, checked_div, isize);
|
checked_impl!(CheckedDiv, checked_div, isize);
|
||||||
|
|
||||||
|
/// Performs a left shift that returns `None` on overflow.
|
||||||
|
pub trait CheckedShl: Sized + Shl<u32, Output=Self> {
|
||||||
|
/// Shifts a number to the left, checking for overflow. If overflow happens,
|
||||||
|
/// `None` is returned.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use num_traits::CheckedShl;
|
||||||
|
///
|
||||||
|
/// let x: u16 = 0x0001;
|
||||||
|
///
|
||||||
|
/// assert_eq!(CheckedShl::checked_shl(&x, 0), Some(0x0001));
|
||||||
|
/// assert_eq!(CheckedShl::checked_shl(&x, 1), Some(0x0002));
|
||||||
|
/// assert_eq!(CheckedShl::checked_shl(&x, 15), Some(0x8000));
|
||||||
|
/// assert_eq!(CheckedShl::checked_shl(&x, 16), None);
|
||||||
|
/// ```
|
||||||
|
fn checked_shl(&self, rhs: u32) -> Option<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! checked_shift_impl {
|
||||||
|
($trait_name:ident, $method:ident, $t:ty) => {
|
||||||
|
impl $trait_name for $t {
|
||||||
|
#[inline]
|
||||||
|
fn $method(&self, rhs: u32) -> Option<$t> {
|
||||||
|
<$t>::$method(*self, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, u8);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, u16);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, u32);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, u64);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, usize);
|
||||||
|
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, i8);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, i16);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, i32);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, i64);
|
||||||
|
checked_shift_impl!(CheckedShl, checked_shl, isize);
|
||||||
|
|
||||||
|
/// Performs a right shift that returns `None` on overflow.
|
||||||
|
pub trait CheckedShr: Sized + Shr<u32, Output=Self> {
|
||||||
|
/// Shifts a number to the left, checking for overflow. If overflow happens,
|
||||||
|
/// `None` is returned.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use num_traits::CheckedShr;
|
||||||
|
///
|
||||||
|
/// let x: u16 = 0x8000;
|
||||||
|
///
|
||||||
|
/// assert_eq!(CheckedShr::checked_shr(&x, 0), Some(0x8000));
|
||||||
|
/// assert_eq!(CheckedShr::checked_shr(&x, 1), Some(0x4000));
|
||||||
|
/// assert_eq!(CheckedShr::checked_shr(&x, 15), Some(0x0001));
|
||||||
|
/// assert_eq!(CheckedShr::checked_shr(&x, 16), None);
|
||||||
|
/// ```
|
||||||
|
fn checked_shr(&self, rhs: u32) -> Option<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, u8);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, u16);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, u32);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, u64);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, usize);
|
||||||
|
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, i8);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, i16);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, i32);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, i64);
|
||||||
|
checked_shift_impl!(CheckedShr, checked_shr, isize);
|
||||||
|
|
Loading…
Reference in New Issue