条款5:理解类型转换
Rust类型转换分为三类:
- 手动:通过实现
From
和Into
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
的项目相同?