我想将
isize
添加到 usize
并包含边界检查,以便结果不会溢出 usize
的边界。这怎么办?
isize::is_negative()
、isize::wrapping_abs()
、usize::checked_add()
和 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)
}
}
isize::is_negative()
vs rhs < 0
?在这种情况下,它不会改变任何东西,因为文字上的逻辑运算算作常量表达式。
但是,虽然允许使用文字,但一般来说是不允许的,因为特征方法不能是
const
。因此,如果您有包装类型,例如Foo(isize)
那么就不允许在 const 上下文中说 foo < Foo(0)
。不过,可以说 foo.is_negative()
,因为 Foo
仍然可以实现 const fn is_negative()
。
是的,你仍然可以说
foo.0 < 0
,但这超出了我想要表达的重点。
实现此目的的一种方法是使用如下函数:
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,
)
}
}
这利用了这样一个事实:当从无符号数的角度来看,这样的加法应该仅在无符号数为负数时“溢出”(即表现出补码行为),并且如果在以下情况下不这样做数字为负数,这表示下溢。
Rustc 1.66+ 支持一些混合整数运算。请注意,这之前在 mixed_integer_ops 功能标志下不稳定。
有了这个你可以做:
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}");
}
这些无符号整数上的
*_add_signed
方法都接受有符号参数并返回无符号结果。有几种变体,具体取决于您想要处理溢出情况的具体程度:
overflowing_
,如上所示,返回一对,其中包含包装结果和指示是否发生溢出的布尔标志。checked_
返回 Option
,溢出时为 None
。saturating_
通过限制最大值和最小值来防止溢出(即 MAX + 1 == MAX
、MIN - 1 == MIN
)。wrapping_
没有解决您的用例 - 它只返回包装的结果,并不指示是否发生溢出。在有符号整数上也有类似的方法,用于接受无符号参数并返回有符号结果的加法或减法,名为
*_add_unsigned
和 *_sub_unsigned
。
(注:改编自我的回答https://stackoverflow.com/a/73153135/6112457)