1pub trait ArithmeticTryOps<RHS = Self> where Self: Sized + core::fmt::Display + core::fmt::Debug + TypeName, RHS: Sized + core::fmt::Display + core::fmt::Debug {
3 fn try_add(self, other: RHS) -> Result<Self, OverflowError<Self, RHS>>;
4 fn try_sub(self, other: RHS) -> Result<Self, OverflowError<Self, RHS>>;
5 fn try_mul(self, other: RHS) -> Result<Self, OverflowError<Self, RHS>>;
6 fn try_div(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
7 fn try_div_euclid(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
8 fn try_rem(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
9 fn try_rem_euclid(self, other: RHS) -> Result<Self, DivisionByZeroError<Self>>;
10 fn try_pow(self, other: u32) -> Result<Self, OverflowError<Self, u32>>;
11 fn try_shl(self, other: u32) -> Result<Self, BigShiftError<Self>>;
13 fn try_shr(self, other: u32) -> Result<Self, BigShiftError<Self>>;
14}
15
16#[derive(Debug, thiserror::Error)]
17#[error("operation {left} {op} {right} overflowed")]
18pub struct OverflowError<L: core::fmt::Display + core::fmt::Debug, R: core::fmt::Display + core::fmt::Debug> {
19 left: L,
20 op: &'static str,
21 right: R,
22}
23
24#[derive(Debug, thiserror::Error)]
25#[error("attempted to divide {0} by zero")]
26pub struct DivisionByZeroError<T: core::fmt::Display + core::fmt::Debug>(T);
27
28#[derive(Debug, thiserror::Error)]
30#[error("operation {left} {op} {right} attempted to shift too much (the type of LHS is {})", L::type_name())]
31pub struct BigShiftError<L: core::fmt::Display + TypeName + core::fmt::Debug> {
32 left: L,
33 op: &'static str,
34 right: u32,
35}
36
37pub trait TypeName {
38 fn type_name() -> &'static str;
39}
40
41macro_rules! impl_type_names {
42 ($($type:ty),*) => {
43 $(
44 impl TypeName for $type {
45 fn type_name() -> &'static str {
46 stringify!($type)
47 }
48 }
49 )*
50 }
51}
52
53macro_rules! impl_overflowing_op {
54 ($try_op:ident, $check_op:ident, $rhs:ty, $op_str:expr) => {
55 fn $try_op(self, other: $rhs) -> Result<Self, OverflowError<Self, $rhs>> {
56 self.$check_op(other).ok_or(OverflowError {
57 left: self,
58 op: $op_str,
59 right: other,
60 })
61 }
62 }
63}
64
65macro_rules! impl_arith_op {
66 ($($type:ty),*) => {
67 $(
68 impl_type_names!($type);
69
70 impl ArithmeticTryOps for $type {
71 impl_overflowing_op!(try_add, checked_add, $type, "+");
72 impl_overflowing_op!(try_sub, checked_sub, $type, "-");
73 impl_overflowing_op!(try_mul, checked_mul, $type, "*");
74 impl_overflowing_op!(try_pow, checked_pow, u32, "**");
76
77 fn try_div(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
78 self.checked_div(other).ok_or(DivisionByZeroError(self))
79 }
80
81 fn try_div_euclid(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
82 self.checked_div_euclid(other).ok_or(DivisionByZeroError(self))
83 }
84
85 fn try_rem(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
86 self.checked_rem(other).ok_or(DivisionByZeroError(self))
87 }
88
89 fn try_rem_euclid(self, other: Self) -> Result<Self, DivisionByZeroError<Self>> {
90 self.checked_rem_euclid(other).ok_or(DivisionByZeroError(self))
91 }
92
93 fn try_shl(self, other: u32) -> Result<Self, BigShiftError<Self>> {
94 self.checked_shl(other).ok_or(BigShiftError {
95 left: self,
96 op: "<<",
97 right: other,
98 })
99 }
100
101 fn try_shr(self, other: u32) -> Result<Self, BigShiftError<Self>> {
102 self.checked_shr(other).ok_or(BigShiftError {
103 left: self,
104 op: ">>",
105 right: other,
106 })
107 }
108 }
109 )*
110 }
111}
112
113impl_arith_op!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
114
115#[cfg(test)]
116mod tests {
117 use super::ArithmeticTryOps;
118
119 #[test]
120 fn add() {
121 assert!(255u8.try_add(1).is_err());
122 }
123}