类型转换,第一部分

我们已经多次强调过,Rust 不会为整数执行隐式类型转换。
那么,如何进行显式转换呢?

as关键字

你可以使用 as 关键字在整数类型之间进行转换。
as 转换是不会失败的。

例如:

#![allow(unused)]
fn main() {
let a: u32 = 10;

// 将 `a` 转换为 `u64` 类型
let b = a as u64;

// 你可以使用 `_` 作为目标类型
// 如果编译器能正确推断出来的话
// 比如:
let c: u64 = a as _;
}

这种转换的语义是你所期望的:所有 u32 的值都是有效的 u64 值。

截断

如果我们反向进行就会更有趣:

#![allow(unused)]
fn main() {
// 一个太大以至于无法放入 `u8` 的数字
let a: u16 = 255 + 1;
let b = a as u8;
}

此程序将无问题运行,因为 as 转换是绝对不会失败的。 但 b 的值是多少呢? 从较大的整数类型转换到较小的类型时,Rust 编译器会执行截断

要了解发生了什么,我们先来看 256u16 在内存中是如何表示的,即一系列位:

 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
|               |               |
+---------------+---------------+
  高8位           低8位

转换为 u8 时,Rust 编译器将保留 u16 内存表示的最后8位:

 0 0 0 0 0 0 0 0 
|               |
+---------------+
  低8位   

因此,256 as u8 等于 0。这在大多数场景下...不太理想。 实际上,如果编译器看到你试图进行会导致截断的字面值转换,它会主动阻止你:

错误:字面值超出 `i8` 的范围
  |
4 |     let a = 255 as i8;
  |             ^^^
  |
  = 注意:字面值 `255` 无法放入范围为 `-128..=127` 的 `i8` 类型中
  = 帮助:考虑使用类型 `u8` 代替
  = 注意:默认启用了 `#[deny(overflowing_literals)]`

建议

总的来说,使用 as 转换要非常小心。
仅限于从较小类型转换到较大类型时使用。若要从较大整数类型转换到较小的整数类型,请依赖于我们将在课程后期探索的可失败的转换机制。

局限性

令人惊讶的行为并不是 as 转换的唯一缺点。它也相当有限:你只能依靠 as 转换用于原始类型和其他少数特殊情况。
在处理复合类型时,你将需要依赖于不同的转换机制(可失败的不可失败的),我们将在后续内容中探讨。

参考资料

  • 本节练习位于 exercises/02_basic_calculator/10_as_casting

进一步阅读

  • 查阅 Rust 官方参考文档的这一部分,了解每种源类型和目标类型组合下 as 转换的确切行为,以及所有允许的转换列表。