Reputation: 82
I want to add an isize
to a usize
and include bounds checking so that the result does not overflow the bounds of a usize
. How can this be done?
Upvotes: 3
Views: 1359
Reputation: 5385
Rustc 1.66+ supports some mixed integer operations. Note, this was previously unstable under the mixed_integer_ops feature flag.
With this you can do:
fn main() {
let x: usize = foo();
let y: isize = bar();
let (res, flag): (usize, bool) = x.overflowing_add_signed(y); // returns a (usize, bool) pair to indicate if overflow occurred
println!("{x} + {y} = {res}, overflowed? {flag}");
}
These *_add_signed
methods on unsigned integers all accept a signed argument and return an unsigned result. There's a few variants, depending on how specifically you want to handle the overflow case:
overflowing_
, shown above, returns a pair, with the wrapped result and a boolean flag indicating if overflow occurred.checked_
returns an Option
, which is None
on overflow.saturating_
prevents overflow by clamping to the max and min values (i.e. MAX + 1 == MAX
, MIN - 1 == MIN
).wrapping_
doesn't address your use case - it only returns the wrapped result and doesn't indicate if overflow occurred.There are also analogous methods on signed integers, for additions or subtractions which accept an unsigned argument and return a signed result, named *_add_unsigned
and *_sub_unsigned
.
(note: adapted from my answer at https://stackoverflow.com/a/73153135/6112457)
Upvotes: 1
Reputation: 26157
You could use a combination of isize::is_negative()
, isize::wrapping_abs()
, usize::checked_add()
and usize::checked_sub()
.
const fn add(lhs: usize, rhs: isize) -> Option<usize> {
if rhs.is_negative() {
lhs.checked_sub(rhs.wrapping_abs() as usize)
} else {
lhs.checked_add(rhs as usize)
}
}
Why isize::is_negative()
vs rhs < 0
? In this case, it doesn't change anything, as logical operations on literals count as constant expressions.
However, while it is allowed for literals it is not allowed in general, as traits methods cannot be const
. So if you had a wrapping type, e.g. Foo(isize)
then it wouldn't be allowed to say foo < Foo(0)
in a const context. Though, it would be possible to say foo.is_negative()
as Foo
could still implement a const fn is_negative()
.
Yes, you'd still be able to say foo.0 < 0
, but that's besides the point I was trying to make.
Upvotes: 5
Reputation: 82
One way to do this would be with a function like the following:
fn signed_unsigned_add(x: usize, y: isize) -> usize {
let (n, overflow) = x.overflowing_add(y as usize);
if (y >= 0) ^ overflow {
n
} else {
panic!(
"signed + unsigned addition overflow: {} + {}",
x,
y,
)
}
}
This takes advantage of the fact that such an addition, when looked at in terms of unsigned numbers, should only "overflow" (i.e. exhibit two's-complement behaviour) when the unsigned number is negative, and if it doesn't do so when the number is negative, this indicates underflow.
Upvotes: 0