【发布时间】:2019-07-21 10:45:49
【问题描述】:
如何将 Rust 中的两个 i16 数字相加或相乘成更大的 i32 而不会溢出?
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: i32 = a + b; // throws EO308 "Expected `u32`, found `u16`
【问题讨论】:
标签: rust integer-overflow
如何将 Rust 中的两个 i16 数字相加或相乘成更大的 i32 而不会溢出?
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: i32 = a + b; // throws EO308 "Expected `u32`, found `u16`
【问题讨论】:
标签: rust integer-overflow
Rust 中没有整体提升或隐式强制转换,因此您必须手动进行所有类型转换。
对于演员表,您可以使用<value> as <type>。但是,如果目标类型是原始类型的超集并且转换它不会丢失信息(例如您的案例),您可以使用 <type>::from 自行记录它:
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: i32 = i32::from(a) + i32::from(b);
assert_eq!(c, 65534);
这种特殊情况不会溢出,但对于其他情况,您可以使用{integer}::checked_*() 函数防止溢出:
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: Option<i16> = a.checked_add(b);
assert_eq!(c, None); //overflow!
请注意,在调试版本中,整数操作溢出默认情况下会出现恐慌。
如果您想要的是包装算术,就像在旧 C 中一样,您可以使用 {integer}::wraping_*(),另外它还适用于有符号和无符号值。
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: i16 = a.wrapping_add(b);
assert_eq!(c, -2);
【讨论】:
rustc 编译器 允许使用开关选择两种行为之一:恐慌或包装;此外,它还默认在 Debug 中恐慌并在 Release 中包装,但用户始终可以更改它。理想情况下,rustc 在默认情况下希望总是恐慌,但是 LLVM 对此类代码的优化很差,而且恐慌的存在通常会阻碍矢量化,因此 Release 的默认值必须包装以匹配 C 性能暂时。
您可以在添加之前将它们都转换为i32。
let c: i32 = (a as i32) + (b as i32);
【讨论】:
From 或 TryFrom 会更习惯用语。
as 工作正常,只是不便于维护。如果a 的类型切换到i64 操作a as i32 将继续编译,现在默默地截断值。 From 仅定义为不会失败的转换,因此会停止编译。 TryFrom 是为所有转换定义的,但会返回一个 Option<Target>,它必须在运行时解包;它通常用于可能失败的转换。