c0nst::c0nst! {
pub c0nst trait AbsDiff: Sized {
type Output;
fn abs_diff(self, other: Self) -> Self::Output;
}
}
macro_rules! abs_diff_impl {
($($t:ty => $out:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl AbsDiff for $t {
type Output = $out;
#[inline]
fn abs_diff(self, other: Self) -> $out {
<$t>::abs_diff(self, other)
}
}
}
)*};
}
abs_diff_impl! {
u8 => u8; u16 => u16; u32 => u32; u64 => u64; usize => usize; u128 => u128;
i8 => u8; i16 => u16; i32 => u32; i64 => u64; isize => usize; i128 => u128;
}
c0nst::c0nst! {
pub c0nst trait UnsignedAbs: Sized {
type Unsigned;
fn unsigned_abs(self) -> Self::Unsigned;
}
}
c0nst::c0nst! {
pub c0nst trait ClampMagnitude: Sized {
type Unsigned;
type Output;
fn clamp_magnitude(self, limit: Self::Unsigned) -> Self::Output;
}
}
macro_rules! unsigned_abs_impl {
($($t:ty => $u:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl UnsignedAbs for $t {
type Unsigned = $u;
#[inline]
fn unsigned_abs(self) -> $u {
<$t>::unsigned_abs(self)
}
}
}
c0nst::c0nst! {
c0nst impl ClampMagnitude for $t {
type Unsigned = $u;
type Output = $t;
#[inline]
fn clamp_magnitude(self, limit: $u) -> $t {
if limit <= <$t>::MAX as $u {
let lim = limit as $t;
if self < -lim {
-lim
} else if self > lim {
lim
} else {
self
}
} else {
self
}
}
}
}
)*};
}
unsigned_abs_impl! {
i8 => u8; i16 => u16; i32 => u32; i64 => u64; isize => usize; i128 => u128;
}
c0nst::c0nst! {
pub c0nst trait CastSigned: Sized {
type Signed;
fn cast_signed(self) -> Self::Signed;
}
}
macro_rules! cast_signed_impl {
($($t:ty => $s:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl CastSigned for $t {
type Signed = $s;
#[inline]
fn cast_signed(self) -> $s {
self as $s
}
}
}
)*};
}
cast_signed_impl! {
u8 => i8; u16 => i16; u32 => i32; u64 => i64; usize => isize; u128 => i128;
}
c0nst::c0nst! {
pub c0nst trait CastUnsigned: Sized {
type Unsigned;
fn cast_unsigned(self) -> Self::Unsigned;
}
}
macro_rules! cast_unsigned_impl {
($($t:ty => $u:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl CastUnsigned for $t {
type Unsigned = $u;
#[inline]
fn cast_unsigned(self) -> $u {
self as $u
}
}
}
)*};
}
cast_unsigned_impl! {
i8 => u8; i16 => u16; i32 => u32; i64 => u64; isize => usize; i128 => u128;
}
c0nst::c0nst! {
pub c0nst trait Widen<T>: Sized {
fn widen(self) -> T;
}
}
macro_rules! widen_impl {
($($from:ty => $($to:ty),+;)*) => {$($(
c0nst::c0nst! {
c0nst impl Widen<$to> for $from {
#[inline]
fn widen(self) -> $to {
self as $to
}
}
}
)+)*};
}
widen_impl! {
u8 => u8, u16, u32, u64, u128, usize;
u16 => u16, u32, u64, u128, usize;
u32 => u32, u64, u128;
u64 => u64, u128;
u128 => u128;
usize => usize;
i8 => i8, i16, i32, i64, i128, isize;
i16 => i16, i32, i64, i128, isize;
i32 => i32, i64, i128;
i64 => i64, i128;
i128 => i128;
isize => isize;
}
c0nst::c0nst! {
pub c0nst trait Truncate<T>: Sized {
fn truncate(self) -> T;
}
}
macro_rules! truncate_impl {
($($from:ty => $($to:ty),+;)*) => {$($(
c0nst::c0nst! {
c0nst impl Truncate<$to> for $from {
#[inline]
fn truncate(self) -> $to {
self as $to
}
}
}
)+)*};
}
truncate_impl! {
u8 => u8;
u16 => u16, u8;
u32 => u32, u16, u8;
u64 => u64, u32, u16, u8;
u128 => u128, u64, u32, u16, u8;
usize => usize, u16, u8;
i8 => i8;
i16 => i16, i8;
i32 => i32, i16, i8;
i64 => i64, i32, i16, i8;
i128 => i128, i64, i32, i16, i8;
isize => isize, i16, i8;
}
c0nst::c0nst! {
pub c0nst trait CheckedCast<T>: Sized {
fn checked_cast(self) -> Option<T>;
}
}
c0nst::c0nst! {
pub c0nst trait StrictCast<T>: Sized {
fn strict_cast(self) -> T;
}
}
c0nst::c0nst! {
pub c0nst trait WrappingCast<T>: Sized {
fn wrapping_cast(self) -> T;
}
}
c0nst::c0nst! {
pub c0nst trait SaturatingCast<T>: Sized {
fn saturating_cast(self) -> T;
}
}
macro_rules! cast_pair_impl {
($from:ty => $($to:ty),*) => {$(
c0nst::c0nst! {
c0nst impl CheckedCast<$to> for $from {
#[inline]
#[allow(unused_comparisons)]
fn checked_cast(self) -> Option<$to> {
let r = self as $to;
if (r as $from) == self && ((r < 0) == (self < 0)) {
Some(r)
} else {
None
}
}
}
}
c0nst::c0nst! {
c0nst impl StrictCast<$to> for $from {
#[inline]
#[track_caller]
fn strict_cast(self) -> $to {
match CheckedCast::<$to>::checked_cast(self) {
Some(v) => v,
None => panic!("attempt to cast integer with overflow"),
}
}
}
}
c0nst::c0nst! {
c0nst impl WrappingCast<$to> for $from {
#[inline]
fn wrapping_cast(self) -> $to {
self as $to
}
}
}
c0nst::c0nst! {
c0nst impl SaturatingCast<$to> for $from {
#[inline]
#[allow(unused_comparisons)]
fn saturating_cast(self) -> $to {
match CheckedCast::<$to>::checked_cast(self) {
Some(v) => v,
None => {
if self < 0 {
<$to>::MIN
} else {
<$to>::MAX
}
}
}
}
}
}
)*};
}
macro_rules! cast_all_impl {
($($from:ty),*) => {$(
cast_pair_impl!($from => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
)*};
}
cast_all_impl!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn abs_diff_unsigned_abs() {
assert_eq!(AbsDiff::abs_diff(100u8, 120), 20);
assert_eq!(AbsDiff::abs_diff(120u8, 100), 20);
assert_eq!(AbsDiff::abs_diff(i8::MIN, i8::MAX), 255u8);
assert_eq!(AbsDiff::abs_diff(-10i32, 10), 20u32);
assert_eq!(UnsignedAbs::unsigned_abs(i128::MIN), 1u128 << 127);
assert_eq!(UnsignedAbs::unsigned_abs(-5isize), 5usize);
}
#[test]
fn clamp_magnitude() {
assert_eq!(ClampMagnitude::clamp_magnitude(120i8, 100), 100);
assert_eq!(ClampMagnitude::clamp_magnitude(-120i8, 100), -100);
assert_eq!(ClampMagnitude::clamp_magnitude(50i8, 100), 50);
assert_eq!(ClampMagnitude::clamp_magnitude(-120i8, 200), -120);
assert_eq!(ClampMagnitude::clamp_magnitude(i8::MIN, 127), -127);
}
#[test]
fn sign_casts() {
assert_eq!(CastSigned::cast_signed(255u8), -1i8);
assert_eq!(CastSigned::cast_signed(127u8), 127i8);
assert_eq!(CastUnsigned::cast_unsigned(-1i8), 255u8);
assert_eq!(CastUnsigned::cast_unsigned(5i8), 5u8);
let c: Option<i8> = CheckedCast::checked_cast(255u8);
assert_eq!(c, None);
let s: i8 = SaturatingCast::saturating_cast(255u8);
assert_eq!(s, 127);
let c: Option<u8> = CheckedCast::checked_cast(-1i8);
assert_eq!(c, None);
let s: u8 = SaturatingCast::saturating_cast(-1i8);
assert_eq!(s, 0);
}
#[test]
#[should_panic(expected = "attempt to cast integer with overflow")]
fn strict_cast_panics() {
let _: u8 = StrictCast::strict_cast(-1i8);
}
#[test]
fn widen_truncate() {
let w: u128 = Widen::widen(u8::MAX);
assert_eq!(w, 255);
let w: i64 = Widen::widen(i32::MIN);
assert_eq!(w, i32::MIN as i64);
let t: u8 = Truncate::truncate(0xABCDu16);
assert_eq!(t, 0xCD);
let t: Option<i8> = CheckedCast::checked_cast(-1000i32);
assert_eq!(t, None);
let t: Option<i8> = CheckedCast::checked_cast(-100i32);
assert_eq!(t, Some(-100));
let t: i8 = SaturatingCast::saturating_cast(376i32);
assert_eq!(t, 127);
let t: i8 = SaturatingCast::saturating_cast(-1000i32);
assert_eq!(t, -128);
}
#[test]
fn generic_casts() {
let r: Option<i8> = CheckedCast::checked_cast(200u8);
assert_eq!(r, None);
let r: Option<u8> = CheckedCast::checked_cast(-1i8);
assert_eq!(r, None);
let r: Option<u8> = CheckedCast::checked_cast(300u16);
assert_eq!(r, None);
let r: Option<u8> = CheckedCast::checked_cast(255u16);
assert_eq!(r, Some(255));
let r: Option<u128> = CheckedCast::checked_cast(-1i8);
assert_eq!(r, None);
let r: Option<i128> = CheckedCast::checked_cast(u64::MAX);
assert_eq!(r, Some(u64::MAX as i128));
let r: u8 = WrappingCast::wrapping_cast(-1i32);
assert_eq!(r, 255);
let r: u8 = SaturatingCast::saturating_cast(-1i32);
assert_eq!(r, 0);
let r: i8 = SaturatingCast::saturating_cast(u128::MAX);
assert_eq!(r, 127);
let r: i8 = SaturatingCast::saturating_cast(i128::MIN);
assert_eq!(r, -128);
let r: Option<usize> = CheckedCast::checked_cast(5usize);
assert_eq!(r, Some(5));
}
#[test]
fn generic_cast_exhaustive_u16_i8() {
for x in 0u16..=u16::MAX {
let checked: Option<i8> = CheckedCast::checked_cast(x);
let expected: Option<i8> = TryInto::try_into(x).ok();
assert_eq!(checked, expected, "{x}");
}
for x in i16::MIN..=i16::MAX {
let checked: Option<u8> = CheckedCast::checked_cast(x);
let expected: Option<u8> = TryInto::try_into(x).ok();
assert_eq!(checked, expected, "{x}");
}
}
}