safe_arith/
lib.rs

1//! Safe arithmetic on integers, avoiding overflow and division by zero.
2//!
3//! ## Why not just use `checked_*` methods?
4//!
5//! Rust's built-in `checked_add`, `checked_mul`, etc. return `Option<T>`, which requires you to
6//! manually handle `None` cases without context about what went wrong. This crate provides:
7//!
8//! 1. **Explicit errors**: Returns `Result<T, ArithError>` with explicit `Overflow` and
9//!    `DivisionByZero` variants
10//! 2. **Mutation in place**: Methods like [`SafeArith::safe_add_assign`] that mutate in place
11//! 3. **Iterator support**: [`SafeArithIter::safe_sum`] method for safely summing iterators
12//!    without overflow
13//!
14//! ## Examples
15//!
16//! ### Basic
17//!
18//! ```rust
19//! use safe_arith::{ArithError, SafeArith};
20//! # use safe_arith::Result;
21//! # fn example() -> Result<()> {
22//!
23//! let x = 10u32;
24//! let y = 20u32;
25//!
26//! // Returns Result<u32, ArithError> instead of Option<u32>
27//! let sum = x.safe_add(y)?;
28//! let diff = y.safe_sub(x)?;
29//! let product = x.safe_mul(y)?;
30//! let quotient = y.safe_div(x)?;
31//!
32//! assert_eq!(u32::MAX.safe_add(1), Err(ArithError::Overflow));
33//!
34//! assert_eq!(x.safe_div(0), Err(ArithError::DivisionByZero));
35//! # Ok(())
36//! # }
37//! ```
38//!
39//! ### Mutating methods
40//!
41//! ```rust
42//! use safe_arith::SafeArith;
43//! # use safe_arith::Result;
44//! # fn example() -> Result<()> {
45//!
46//! let mut x = 10u32;
47//! x.safe_add_assign(5)?;
48//! x.safe_mul_assign(2)?;
49//! assert_eq!(x, 30);
50//! # Ok(())
51//! # }
52//! ```
53//!
54//! ### Safe sum for iterators
55//!
56//! ```rust
57//! use safe_arith::SafeArithIter;
58//! # use safe_arith::Result;
59//! # fn example() -> Result<()> {
60//!
61//! let numbers = vec![1u64, 2, 3, 4, 5];
62//! let total = numbers.into_iter().safe_sum()?;
63//!
64//! let overflow = vec![u64::MAX, 1];
65//! assert!(overflow.into_iter().safe_sum().is_err());
66//! # Ok(())
67//! # }
68//! ```
69mod iter;
70
71pub use iter::SafeArithIter;
72
73/// Error representing the failure of an arithmetic operation.
74#[derive(Debug, PartialEq, Eq, Clone, Copy)]
75pub enum ArithError {
76    Overflow,
77    DivisionByZero,
78}
79
80pub type Result<T> = std::result::Result<T, ArithError>;
81
82macro_rules! assign_method {
83    ($name:ident, $op:ident, $doc_op:expr) => {
84        assign_method!($name, $op, Self, $doc_op);
85    };
86    ($name:ident, $op:ident, $rhs_ty:ty, $doc_op:expr) => {
87        #[doc = "Safe variant of `"]
88        #[doc = $doc_op]
89        #[doc = "`."]
90        #[inline]
91        fn $name(&mut self, other: $rhs_ty) -> Result<()> {
92            *self = self.$op(other)?;
93            Ok(())
94        }
95    };
96}
97
98/// Trait providing safe arithmetic operations for built-in types.
99pub trait SafeArith<Rhs = Self>: Sized + Copy {
100    const ZERO: Self;
101    const ONE: Self;
102
103    /// Safe variant of `+` that guards against overflow.
104    fn safe_add(&self, other: Rhs) -> Result<Self>;
105
106    /// Safe variant of `-` that guards against overflow.
107    fn safe_sub(&self, other: Rhs) -> Result<Self>;
108
109    /// Safe variant of `*` that guards against overflow.
110    fn safe_mul(&self, other: Rhs) -> Result<Self>;
111
112    /// Safe variant of `/` that guards against division by 0.
113    fn safe_div(&self, other: Rhs) -> Result<Self>;
114
115    /// Safe variant of `%` that guards against division by 0.
116    fn safe_rem(&self, other: Rhs) -> Result<Self>;
117
118    /// Safe variant of `<<` that guards against overflow.
119    fn safe_shl(&self, other: u32) -> Result<Self>;
120
121    /// Safe variant of `>>` that guards against overflow.
122    fn safe_shr(&self, other: u32) -> Result<Self>;
123
124    assign_method!(safe_add_assign, safe_add, Rhs, "+=");
125    assign_method!(safe_sub_assign, safe_sub, Rhs, "-=");
126    assign_method!(safe_mul_assign, safe_mul, Rhs, "*=");
127    assign_method!(safe_div_assign, safe_div, Rhs, "/=");
128    assign_method!(safe_rem_assign, safe_rem, Rhs, "%=");
129    assign_method!(safe_shl_assign, safe_shl, u32, "<<=");
130    assign_method!(safe_shr_assign, safe_shr, u32, ">>=");
131}
132
133macro_rules! impl_safe_arith {
134    ($typ:ty) => {
135        impl SafeArith for $typ {
136            const ZERO: Self = 0;
137            const ONE: Self = 1;
138
139            #[inline]
140            fn safe_add(&self, other: Self) -> Result<Self> {
141                self.checked_add(other).ok_or(ArithError::Overflow)
142            }
143
144            #[inline]
145            fn safe_sub(&self, other: Self) -> Result<Self> {
146                self.checked_sub(other).ok_or(ArithError::Overflow)
147            }
148
149            #[inline]
150            fn safe_mul(&self, other: Self) -> Result<Self> {
151                self.checked_mul(other).ok_or(ArithError::Overflow)
152            }
153
154            #[inline]
155            fn safe_div(&self, other: Self) -> Result<Self> {
156                self.checked_div(other).ok_or(ArithError::DivisionByZero)
157            }
158
159            #[inline]
160            fn safe_rem(&self, other: Self) -> Result<Self> {
161                self.checked_rem(other).ok_or(ArithError::DivisionByZero)
162            }
163
164            #[inline]
165            fn safe_shl(&self, other: u32) -> Result<Self> {
166                self.checked_shl(other).ok_or(ArithError::Overflow)
167            }
168
169            #[inline]
170            fn safe_shr(&self, other: u32) -> Result<Self> {
171                self.checked_shr(other).ok_or(ArithError::Overflow)
172            }
173        }
174    };
175}
176
177impl_safe_arith!(u8);
178impl_safe_arith!(u16);
179impl_safe_arith!(u32);
180impl_safe_arith!(u64);
181impl_safe_arith!(usize);
182impl_safe_arith!(i8);
183impl_safe_arith!(i16);
184impl_safe_arith!(i32);
185impl_safe_arith!(i64);
186impl_safe_arith!(isize);
187
188#[cfg(test)]
189mod test {
190    use super::*;
191
192    #[test]
193    fn basic() {
194        let x = 10u32;
195        let y = 11;
196        assert_eq!(x.safe_add(y), Ok(x + y));
197        assert_eq!(y.safe_sub(x), Ok(y - x));
198        assert_eq!(x.safe_mul(y), Ok(x * y));
199        assert_eq!(x.safe_div(y), Ok(x / y));
200        assert_eq!(x.safe_rem(y), Ok(x % y));
201
202        assert_eq!(x.safe_shl(1), Ok(x << 1));
203        assert_eq!(x.safe_shr(1), Ok(x >> 1));
204    }
205
206    #[test]
207    fn mutate() {
208        let mut x = 0u8;
209        x.safe_add_assign(2).unwrap();
210        assert_eq!(x, 2);
211        x.safe_sub_assign(1).unwrap();
212        assert_eq!(x, 1);
213        x.safe_shl_assign(1).unwrap();
214        assert_eq!(x, 2);
215        x.safe_mul_assign(3).unwrap();
216        assert_eq!(x, 6);
217        x.safe_div_assign(4).unwrap();
218        assert_eq!(x, 1);
219        x.safe_shr_assign(1).unwrap();
220        assert_eq!(x, 0);
221    }
222
223    #[test]
224    fn errors() {
225        assert!(u32::MAX.safe_add(1).is_err());
226        assert!(u32::MIN.safe_sub(1).is_err());
227        assert!(u32::MAX.safe_mul(2).is_err());
228        assert!(u32::MAX.safe_div(0).is_err());
229        assert!(u32::MAX.safe_rem(0).is_err());
230        assert!(u32::MAX.safe_shl(32).is_err());
231        assert!(u32::MAX.safe_shr(32).is_err());
232    }
233}