第5条:理解类型转换Rust的类型转换分为三个类别:手动:通过实现From和Intotrait提供的用户定义类型转换半自动:使用as关键字在值之间进行显式转换自动:隐式强制转换为新类型本章节的重点主要是第一种,即手动转换类型,因为后两种大多数情况下不适用于用户定义类
Rust 的类型转换分为三个类别:
From
和 Into trait
提供的用户定义类型转换as
关键字在值之间进行显式转换本章节的重点主要是第一种,即手动转换类型,因为后两种大多数情况下不适用于用户定义类型的转换。但也有一些例外,所以本章节最后的部分将讨论转换和强制类型转换 —— 包括它们如何适用于用户定义的类型。
请注意,与许多较旧的语言不同,Rust
在数值类型之间不会执行自动转换。这甚至适用于整数类型的“安全”转换:
let x: u32 = 2;
let y: u64 = x;
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...
变体的区别在于,唯一的 trait
方法返回一个 Result
而不是保证的新项。Try...
trait
定义还要求一个关联类型,它给出了失败情况下发出的错误 E
的类型。
因此,第一条建议是,如果可能转换失败,则实现(仅)Try... trait
,与[第 4 条] 一致。另一种方法是忽略错误的可能性(例如,使用 .unwrap()
),但这需要是深思熟虑的选择,在大多数情况下,最好将这个选择留给调用者。
类型转换 trait
具有明显的对称性:如果类型 T
可以转换为类型 U
(通过 Into<U>
),难道这不等于可以通过从类型 T
的项转换来创建类型 U
的项(通过 From<T>
)吗?
确实如此,这导致了第二条建议:为转换实现 From trait
。Rust
标准库必须在这两个可能性中选择一个,以防止系统在眩晕的圆圈中旋转,[^1] 它选择了自动提供 From
实现的 Into
。
如果你正在使用这两个 trait
中的一个,作为你自己新的泛型的 trait
约束,那么建议是相反的:对 trait
约束使用 Into trait
。这样,约束将同时满足直接实现 Into
的内容和仅直接实现 From
的内容。
From
和 Into
的文档强调了这种自动转换,但阅读标准库代码的相关部分也值得一读,这是一个泛型 trait
实现:
impl<T, U> Into<U> for T
where
U: From<T>,
{
fn into(self) -> U {
U::from(self)
}
}
将 trait
规范翻译成文字可以帮助理解更复杂的 trait
约束。在这个案例中,它相当简单:"只要 U
已经实现了 From<T>
,我就可以为类型 T
实现 Into<U>
"。
标准库还包括了为标准库类型实现这些转换 trait
的各种情况。正如你所预期的,对于整数转换,当目标类型包括源类型的所有可能值时(例如,u64
的 From<u32>
),会有 From
实现,而当源值可能不适合目标时(例如,u32
的 TryFrom<u64>
),会有 TryFrom
实现。
除了前面显示的 Into
版本的泛型 trait
实现之外,还有各种其他的泛型 trait
实现主要用于智能指针类型,允许智能指针从其持有的类型的实例自动构造。这意味着接受智能指针参数的泛型方法也可以用普通的旧项调用;更多内容将在后续介绍和[第 8 条]中展开。
TryFrom trait
还有一个泛型实现,适用于任何已经以...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!