I have a variable on which I do a lot of arithmetic operations.
My code was something like this:
let a: u32 = 3;
let res = (a*2) - 42
I had an overflow when a
is too low, so I had to use saturating_sub()
to solve the issue:
let a: u32 = 3;
let res = (a * 2).saturating_sub(42);
This does work very well, but saturating_sub
is a bit less readable than -
.
Since I have a lot of these operations, and that in every cases, I want a
to saturate. I was wondering if it is possible to define that a
should always saturate. And not replace every -
with saturate_sub
.
That's std::num::Saturating
, however it only implements operations with other Saturating
, not the underlying primitive:
let a = Saturating(3u32);
let res = (a * Saturating(2)) - Saturating(42);
If that's a problem for you, you can create your own type:
use std::num::Saturating as StdSaturating;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
#[derive(Clone, Copy, Debug)]
pub struct Saturating<T>(pub T);
macro_rules! impl_saturating_op {
( $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident => $($ty:ty),* ) => {
$(
impl $trait for Saturating<$ty> {
type Output = Saturating<$ty>;
fn $method(self, other: Saturating<$ty>) -> Self::Output {
Saturating(StdSaturating(self.0).$method(StdSaturating(other.0)).0)
}
}
impl $trait<$ty> for Saturating<$ty> {
type Output = Saturating<$ty>;
fn $method(self, other: $ty) -> Self::Output {
Saturating(StdSaturating(self.0).$method(StdSaturating(other)).0)
}
}
impl $trait<Saturating<$ty>> for $ty {
type Output = Saturating<$ty>;
fn $method(self, other: Saturating<$ty>) -> Self::Output {
Saturating(StdSaturating(self).$method(StdSaturating(other.0)).0)
}
}
impl $trait_assign for Saturating<$ty> {
fn $method_assign(&mut self, other: Saturating<$ty>) {
self.0 = StdSaturating(self.0).$method(StdSaturating(other.0)).0;
}
}
impl $trait_assign<$ty> for Saturating<$ty> {
fn $method_assign(&mut self, other: $ty) {
self.0 = StdSaturating(self.0).$method(StdSaturating(other)).0;
}
}
)*
}
}
impl_saturating_op!(Add, add, AddAssign, add_assign => i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
impl_saturating_op!(Sub, sub, SubAssign, sub_assign => i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
impl_saturating_op!(Mul, mul, MulAssign, mul_assign => i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
impl_saturating_op!(Div, div, DivAssign, div_assign => i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
fn main() {
let a = Saturating(3u32);
let res = (a * 2) - 42;
}