Add checked shifts

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:
Fabian Schuiki 2018-01-03 16:25:13 +01:00
parent 42a610d323
commit 21dfae004c
2 changed files with 54 additions and 1 deletions

View File

@ -21,6 +21,8 @@ pub trait PrimInt
+ CheckedSub<Output=Self>
+ CheckedMul<Output=Self>
+ CheckedDiv<Output=Self>
+ CheckedShl<usize, Output=Self>
+ CheckedShr<usize, Output=Self>
+ Saturating
{
/// Returns the number of ones in the binary representation of `self`.

View File

@ -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
/// overflow.
@ -90,3 +90,54 @@ checked_impl!(CheckedDiv, checked_div, i32);
checked_impl!(CheckedDiv, checked_div, i64);
checked_impl!(CheckedDiv, checked_div, isize);
/// Performs a left shift that returns `None` on overflow.
pub trait CheckedShl<RHS>: Sized + Shl<RHS, Output=Self> {
/// Shifts a number to the left, checking for overflow. If overflow happens, `None` is
/// returned.
fn checked_shl(&self, rhs: &RHS) -> Option<Self>;
}
macro_rules! checked_shift_impl {
($trait_name:ident, $method:ident, $rhs:ty, $t:ty) => {
impl $trait_name<$rhs> for $t {
#[inline]
fn $method(&self, rhs: &$rhs) -> Option<$t> {
// Note the cast to `u32` here: The standard library is somewhat inconsistent here.
// The `Shl<T>` and `Shr<T>` trait are generic over the right-hand side `T`, but
// the checked versions of the shifts all operate on `u32`.
// TODO: Maybe we should use a conversion that can fail here. This would allow us
// to catch the case where `rhs` exceeds the `u32` accepted by the stdlib, and
// return a `None` instead.
<$t>::$method(*self, *rhs as u32)
}
}
}
}
macro_rules! checked_shift_impl_all {
($trait_name:ident, $method:ident, $($t:ty)*) => ($(
checked_shift_impl! { $trait_name, $method, u8 , $t }
checked_shift_impl! { $trait_name, $method, u16 , $t }
checked_shift_impl! { $trait_name, $method, u32 , $t }
checked_shift_impl! { $trait_name, $method, u64 , $t }
checked_shift_impl! { $trait_name, $method, usize, $t }
checked_shift_impl! { $trait_name, $method, i8 , $t }
checked_shift_impl! { $trait_name, $method, i16 , $t }
checked_shift_impl! { $trait_name, $method, i32 , $t }
checked_shift_impl! { $trait_name, $method, i64 , $t }
checked_shift_impl! { $trait_name, $method, isize, $t }
)*)
}
checked_shift_impl_all!(CheckedShl, checked_shl, u8 u16 u32 u64 usize i8 i16 i32 i64 isize);
/// Performs a right shift that returns `None` on overflow.
pub trait CheckedShr<RHS>: Sized + Shr<RHS, Output=Self> {
/// Shifts a number to the left, checking for overflow. If overflow happens, `None` is
/// returned.
fn checked_shr(&self, rhs: &RHS) -> Option<Self>;
}
checked_shift_impl_all!(CheckedShr, checked_shr, u8 u16 u32 u64 usize i8 i16 i32 i64 isize);