pub trait SaturatingCast: Sized {
#[inline]
fn saturating_cast<Dst>(self) -> Dst
where
Dst: SaturatingFrom<Self>,
{
Dst::saturating_from(self)
}
}
macro_rules! impl_saturating_cast {
($($src:ty),* $(,)?) => {
$( impl SaturatingCast for $src {} )*
};
}
impl_saturating_cast!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
pub trait SaturatingFrom<Src> {
fn saturating_from(src: Src) -> Self;
}
macro_rules! impl_saturating_from {
(unsigned: $($src:ty),* $(,)?) => {
$( impl_saturating_from!(@unsigned $src => i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); )*
};
(signed: $($src:ty),* $(,)?) => {
$( impl_saturating_from!(@signed $src => i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); )*
};
(@unsigned $src:ty => $($dst:ty),*) => {
$(
impl SaturatingFrom<$src> for $dst {
#[inline]
fn saturating_from(src: $src) -> Self {
<$dst>::try_from(src).unwrap_or(<$dst>::MAX)
}
}
)*
};
(@signed $src:ty => $($dst:ty),*) => {
$(
impl SaturatingFrom<$src> for $dst {
#[inline]
fn saturating_from(src: $src) -> Self {
<$dst>::try_from(src).unwrap_or(if src < 0 { <$dst>::MIN } else { <$dst>::MAX })
}
}
)*
};
}
impl_saturating_from!(unsigned: u8, u16, u32, u64, u128, usize);
impl_saturating_from!(signed: i8, i16, i32, i64, i128, isize);
#[cfg(test)]
mod tests {
use super::SaturatingCast as _;
#[test]
fn widening_never_clamps() {
assert_eq!(7_u32.saturating_cast::<i64>(), 7);
assert_eq!(0_u8.saturating_cast::<u64>(), 0);
assert_eq!(u8::MAX.saturating_cast::<u16>(), u16::from(u8::MAX));
assert_eq!((-3_i8).saturating_cast::<i64>(), -3);
assert_eq!(i32::MIN.saturating_cast::<i64>(), i64::from(i32::MIN));
}
#[test]
fn same_type_is_identity() {
assert_eq!(123_u64.saturating_cast::<u64>(), 123);
assert_eq!((-123_i64).saturating_cast::<i64>(), -123);
assert_eq!(u64::MAX.saturating_cast::<u64>(), u64::MAX);
}
#[test]
fn unsigned_saturates_to_max() {
assert_eq!(300_u32.saturating_cast::<u8>(), u8::MAX);
assert_eq!(u64::MAX.saturating_cast::<i64>(), i64::MAX);
assert_eq!(u64::MAX.saturating_cast::<i32>(), i32::MAX);
assert_eq!(u128::MAX.saturating_cast::<u64>(), u64::MAX);
}
#[test]
fn signed_saturates_both_ends() {
assert_eq!(1000_i32.saturating_cast::<i8>(), i8::MAX);
assert_eq!(1000_i32.saturating_cast::<u8>(), u8::MAX);
assert_eq!((-1000_i32).saturating_cast::<i8>(), i8::MIN);
assert_eq!((-1_i32).saturating_cast::<u8>(), 0);
assert_eq!((-1_i64).saturating_cast::<u64>(), 0);
assert_eq!(i128::MIN.saturating_cast::<u128>(), 0);
}
#[test]
fn usize_isize() {
assert_eq!(usize::MAX.saturating_cast::<i16>(), i16::MAX);
assert_eq!(300_usize.saturating_cast::<u8>(), u8::MAX);
assert_eq!(isize::MIN.saturating_cast::<i8>(), i8::MIN);
assert_eq!(5_isize.saturating_cast::<u8>(), 5);
}
#[test]
fn matches_call_site_conversions() {
assert_eq!(u64::MAX.saturating_cast::<i64>(), i64::MAX);
assert_eq!(0_u64.saturating_cast::<i64>(), 0);
assert_eq!(42_u64.saturating_cast::<i64>(), 42);
assert_eq!(10_usize.saturating_cast::<i16>(), 10);
assert_eq!(usize::MAX.saturating_cast::<i16>(), i16::MAX);
}
#[test]
fn works_through_a_reference() {
let value = u64::MAX;
let by_ref: &u64 = &value;
assert_eq!(by_ref.saturating_cast::<i64>(), i64::MAX);
}
}