条款5:理解类型转换

Rust类型转换分为三类:

  • 手动:通过实现 FromInto trait提供的用户定义类型转换
  • 半自动:使用 as 关键字在值之间进行显式转换
  • 自动:隐式强制转换为新类型

本条款的大部分内容侧重于第一种,即手动类型转换,因为后两种类型大多不适用于用户自定义类型的转换。对此有几个例外,因此本条款末尾的部分讨论了强制转换 - 包括它们如何应用于用户定义类型。

请注意,与许多较旧的语言不同,Rust不会在数字类型之间执行自动转换。这甚至适用于整数类型的“安全”转换:

#![allow(unused)]
fn main() {
let x: u32 = 2;
let y: u64 = x;
}
#![allow(unused)]
fn main() {
error[E0308]: mismatched types
  --> src/main.rs:70:18
   |
70 |     let y: u64 = x;
   |            ---   ^ expected `u64`, found `u32`
   |            |
   |            expected due to this
   |
help: you can convert a `u32` to a `u64`
   |
70 |     let y: u64 = x.into();
   |                   +++++++
}

用户定义类型转换

与该语言的其他特性(第 10 条)一样,在不同用户定义类型的值之间执行转换的能力被封装为标准trait — 或者更确切地说,是一组相关的通用trait。

表达转换类型值的能力的四个相关trait如下:

  • From<T>:此类型的项可以从类型 T 的项构建,转换始终成功。
  • TryFrom<T>:此类型的项可以从类型 T 的项构建,但转换可能不会成功。
  • Into<T>:此类型的项可以转换为类型 T 的项,转换始终成功。
  • TryInto<T>:此类型的项可以转换为类型 T 的项,但转换可能不会成功。

鉴于第 1 条中关于在类型系统中表达事物的讨论,发现 Try... 变体的不同之处在于唯一的特征方法返回结果而不是保证的新项,这并不奇怪。 Try... 特征定义还需要一个关联类型,该类型给出在失败情况下发出的错误 E 的类型。

因此,第一条建议是,如果转换可能失败,则(仅)实现 Try... 特征,与第 4 条一致。另一种方法是忽略错误的可能性(例如,使用 .unwrap()),但这需要深思熟虑,在大多数情况下,最好将该选择留给调用者。

类型转换特征具有明显的对称性:如果类型 T 可以转换为类型 U(通过 Into<U>),那么这是否与可以通过从类型 T 的项目(通过 From<T>)转换来创建类型 U 的项目相同?