类型转换,第一部分 (Conversions, pt. 1)
我们一遍又一遍地强调过,Rust 不会对整数 (integer) 执行
隐式的类型转换 (implicit type conversion)。
那么如何执行_显式_的类型转换呢?
as
你可以使用 as 运算符在不同的整数类型 (integer type) 之间进行转换。
as 转换是绝对成功的 (infallible)。
例如:
let a: u32 = 10;
// 把 `a` 转换为 `u64` 类型
let b = a as u64;
// 如果编译器能正确推断出目标类型,
// 你也可以使用 `_` 作为目标类型。
// 例如:
let c: u64 = a as _;
这个转换的语义符合你的预期:所有的 u32 值都是合法的 u64
值。
截断 (Truncation)
如果反向转换,事情就有趣了:
// 一个无法装入 `u8` 的
// 数字
let a: u16 = 255 + 1;
let b = a as u8;
这个程序运行时不会有任何问题,因为 as 转换是绝对成功的 (infallible)。
但 b 的值是什么?
当从一个较大的整数类型 (integer type) 转到较小的整数类型时,Rust 编译器 (compiler) 会执行
截断 (truncation)。
为了理解发生了什么,让我们先看看 256u16 在内存中
是如何以位序列表示的:
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
| | |
+---------------+---------------+
前 8 位 后 8 位
转换为 u8 时,Rust 编译器 (compiler) 会保留 u16
内存表示的后 8 位:
0 0 0 0 0 0 0 0
| |
+---------------+
后 8 位
因此 256 as u8 等于 0。在大多数情况下,这……并不理想。
事实上,如果 Rust 编译器 (compiler) 看到你试图
对一个会导致截断 (truncation) 的字面量值进行类型转换,它会主动尝试阻止你:
error: literal out of range for `i8`
|
4 | let a = 255 as i8;
| ^^^
|
= note: the literal `255` does not fit into the type `i8`
whose range is `-128..=127`
= help: consider using the type `u8` instead
= note: `#[deny(overflowing_literals)]` on by default
建议 (Recommendation)
作为一条经验法则,使用 as 转换时要相当小心。
_只_在从较小类型转换到较大类型时使用它。
要从较大整数类型转换到较小整数类型,请依赖
我们将在课程后面探讨的_可失败_转换机制 (fallible conversion machinery)。
局限性 (Limitations)
行为出人意料并不是 as 转换的唯一缺点。
它的适用范围也相当有限:你只能对原始类型 (primitive type) 和少数其他特殊情况
依赖 as 转换。
处理复合类型 (composite type) 时,你必须依赖
不同的转换机制(可失败的
和绝对成功的),我们将在后面探讨。
进一步阅读
- 查看 Rust 官方参考手册
以了解
as转换在每种源/目标类型组合下的精确行为, 以及允许的转换的详尽列表。
原文链接:英文原文