Skip to main content

use_arithmetic/
checked.rs

1#![allow(clippy::module_name_repetitions)]
2
3mod sealed {
4    pub trait Sealed {}
5
6    macro_rules! impl_sealed {
7        ($($ty:ty),* $(,)?) => {
8            $(impl Sealed for $ty {})*
9        };
10    }
11
12    impl_sealed!(
13        u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
14    );
15}
16
17/// Primitive integer types supported by the checked arithmetic wrappers.
18pub trait CheckedArithmetic: sealed::Sealed + Sized {
19    /// Returns `self + rhs`, or `None` on overflow.
20    fn checked_add(self, rhs: Self) -> Option<Self>;
21
22    /// Returns `self - rhs`, or `None` on overflow.
23    fn checked_sub(self, rhs: Self) -> Option<Self>;
24
25    /// Returns `self * rhs`, or `None` on overflow.
26    fn checked_mul(self, rhs: Self) -> Option<Self>;
27}
28
29macro_rules! impl_checked_arithmetic {
30    ($($ty:ty),* $(,)?) => {
31        $(impl CheckedArithmetic for $ty {
32            fn checked_add(self, rhs: Self) -> Option<Self> {
33                <$ty>::checked_add(self, rhs)
34            }
35
36            fn checked_sub(self, rhs: Self) -> Option<Self> {
37                <$ty>::checked_sub(self, rhs)
38            }
39
40            fn checked_mul(self, rhs: Self) -> Option<Self> {
41                <$ty>::checked_mul(self, rhs)
42            }
43        })*
44    };
45}
46
47impl_checked_arithmetic!(
48    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
49);
50
51/// Returns `left + right`, or `None` on overflow.
52#[must_use]
53pub fn checked_add<T: CheckedArithmetic>(left: T, right: T) -> Option<T> {
54    <T as CheckedArithmetic>::checked_add(left, right)
55}
56
57/// Returns `left - right`, or `None` on overflow.
58#[must_use]
59pub fn checked_sub<T: CheckedArithmetic>(left: T, right: T) -> Option<T> {
60    <T as CheckedArithmetic>::checked_sub(left, right)
61}
62
63/// Returns `left * right`, or `None` on overflow.
64#[must_use]
65pub fn checked_mul<T: CheckedArithmetic>(left: T, right: T) -> Option<T> {
66    <T as CheckedArithmetic>::checked_mul(left, right)
67}
68
69#[cfg(test)]
70mod tests {
71    use super::{checked_add, checked_mul, checked_sub};
72
73    #[test]
74    fn reports_overflow_for_unsigned_values() {
75        assert_eq!(checked_add(u8::MAX, 1), None);
76        assert_eq!(checked_sub(0_u8, 1), None);
77        assert_eq!(checked_mul(200_u8, 2), None);
78    }
79
80    #[test]
81    fn reports_overflow_for_signed_values() {
82        assert_eq!(checked_add(i8::MAX, 1), None);
83        assert_eq!(checked_sub(i8::MIN, 1), None);
84        assert_eq!(checked_mul(i8::MAX, 2), None);
85    }
86}