我知道rust has mixed integer operations,但我找不到一种直接的方法来获得两个无符号整数的有符号差,同时正确处理溢出:

// one or both values may be too large to just cast to isize
let x: usize = (isize::MAX as usize) + 5;
let y: usize = (isize::MAX as usize) + 7;
let d: isize = x.signed_saturating_sub(y); // non-existent method
assert_eq!(d, -2);

我可以try 以强制转换的方式实现它,但我不确定如何正确检测溢出:


trait SignedSub {
    type Signed;
    fn signed_overflowing_sub(self, rhs: Self) -> (Self::Signed, bool);
    fn signed_wrapping_sub(self, rhs: Self) -> Self::Signed;
    fn signed_saturating_sub(self, rhs: Self) -> Self::Signed;
    fn signed_checked_sub(self, rhs: Self) -> Option<Self::Signed>;
}
impl SignedSub for usize {
    type Signed = isize;

    fn signed_overflowing_sub(self, rhs: Self) -> (Self::Signed, bool) {
        let (abs, neg) = if self < rhs {
            (rhs - self, true)
        } else {
            (self - rhs, false)
        };
        let abs = abs as isize;
        let res = match neg {
            true => -abs,
            false => abs,
        };
        (res, todo!("how to tell if it overflowed isize?"))
    }

    fn signed_wrapping_sub(self, rhs: Self) -> Self::Signed {
        self.signed_overflowing_sub(rhs).0
    }

    fn signed_saturating_sub(self, rhs: Self) -> Self::Signed {
        let (r, overflowed) = self.signed_overflowing_sub(rhs);
        match overflowed {
            true => match self.cmp(&rhs) {
                Ordering::Less => isize::MIN,
                Ordering::Equal => unreachable!(),
                Ordering::Greater => isize::MAX,
            },
            false => r,
        }
    }

    fn signed_checked_sub(self, rhs: Self) -> Option<Self::Signed> {
        let (r, overflowed) = self.signed_overflowing_sub(rhs);
        match overflowed {
            true => None,
            false => Some(r),
        }
    }
}

#[cfg(test)]
mod test {
    use super::SignedSub;
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn it_works(a: usize, b: isize) {
            // a + b = c
            // a + b - a = c - a
            // b = c - a
            let (c,flag) = a.overflowing_add_signed(b);
            let (b2, flag2) = c.signed_overflowing_sub(a);
            // the flags should be the same
            assert_eq!((b, flag), (b2, flag2));
        }
    }
}

如上面的测试所示,它在概念上与uX::overflowing_add_signed(iX)方法相反,并且在相同的情况下会溢出.

推荐答案

您还可以在usize::overflowing_sub的基础上构建以下内容:

fn signed_overflowing_sub(self, rhs: Self) -> (Self::Signed, bool) {
    let (res, overflowed) = self.overflowing_sub(rhs);
    let res = res as Self::Signed;
    (res, overflowed ^ (res < 0))
}
  • 如果减法没有溢出usize,则结果为正,当其超过isize::MAX时恰好溢出isize,这相当于强制转换为isize时为负数.

  • 如果减法确实溢出usize,则结果为负,并且正好在小于isize::MIN时溢出isize,这相当于在转换为isize时为正.

Correctness proof on u8/i8

Rust相关问答推荐

如何在rust中有条件地分配变量?

如何从Rust记录WASM堆内存使用情况?

使用模块中的所有模块,但不包括特定模块

为什么std repeat trait绑定在impl块和关联函数之间?

使用极点数据帧时,找不到枚举结果的方法lazy()

`Pin`有没有不涉及不安全代码的目的?

如何在函数中返回自定义字符串引用?

Rust面向对象设计模式

为什么比较Option<;字符串>;具有常数Option<&;str>;需要显式类型转换吗?

当我编译 Rust 代码时,我是否缺少 AVX512 的目标功能?

Rust 中多个 & 符号的内存表示

需要一个有序向量来进行 struct 初始化

sha256 摘要仅适用于 &*

`移动||异步移动{...}`,如何知道哪个移动正在移动哪个?

特征中定义的类型与一般定义的类型之间的区别

Rust 函数指针似乎被borrow 判断器视为有状态的

Rust,我如何正确释放堆分配的内存?

为什么这个 Trait 无效?以及改用什么签名?

在同一向量 Rust 中用另一个字符串扩展一个字符串

为什么 Rust 中的关联类型需要明确的生命周期注释?