托尼托尼·乔巴 和 凯撒·库朗
科学无非就是在自然界的多样性中寻求统一性(或者更确切地说,是在我们经验的多样性中寻求统一性)。用 Coleridge 的话说,诗歌、绘画、艺术,同样是在多样性中寻求统一性
——Jacob Bronowski
Rust “实用工具” trait,这是标准库中各种 trait 的“百宝箱”,它们对 Rust 的编写方式有相当大的影响,所以,只有熟悉它们,你才能写出符合 Rust 语言惯例的代码并据此为你的 crate 设计公共接口,让用户认为这些接口是符合 Rust 风格的
语言扩展trait
运算符重载trait能让你在自己的类型上使用 Rust 的表达式运算符,同样,还有其他几个标准库 trait 也是 Rust 的扩展点,允许你把自己的类型更紧密地集成进语言中。这类trait包括 Drop
、Deref
和 DerefMut
,以及转换trait From
和 Into
语言扩展trait汇总表
trait | 描述 |
---|---|
Drop |
析构器。每当丢弃一个值时,Rust 都要自动运行的清理代码 |
Deref 与 DerefMut |
智能指针类型的trait |
From 与 Into |
用于将一种类型的值转换为另一种类型的转换trait |
TryFrom 与 TryInto |
用于将一种类型的值转换为另一种类型的转换trait,用于可能失败的转换 |
Drop
Drop 是标准库内置的,也是一个特殊的 trait,它定义了一个叫做 drop
的方法。这个方法在值离开作用域时被自动调用。这个特性可以用来执行一些清理工作,比如释放资源
1 | struct MyType; |
在这个例子中,我们定义了一个 MyType 类型,并为它实现了 Drop trait。当 my_instance 离开作用域时,drop
方法会被自动调用,打印出 “Dropping MyType”
注意!
Rust的Drop trait是在值离开作用域时被自动调用的,而不是在值被销毁时。这意味着,如果一个值被移动到另一个作用域,它的drop方法不会被调用
当一个值的拥有者消失时,Rust 会丢弃(drop)该值。丢弃一个值就必须释放该值拥有的任何其他值、堆存储和系统资源。丢弃可能发生在多种情况下:当变量超出作用域时;在表达式语句的末尾;当截断一个向量时,会从其末尾移除元素;等等
Deref 与 DerefMut
通过实现 std::ops::Deref
trait 和 std::ops::DerefMut
trait ,可以指定像 *
和 .
这样的解引用运算符在你的类型上的行为
在Rust中,Deref
和 DerefMut
是两个 trait,它们允许我们重载解引用运算符 *
和 *mut
1. Deref
trait:它定义了一个叫做 deref
的方法,这个方法返回一个引用。当我们对一个实现了Deref
trait的类型使用 *
运算符时,deref
方法会被自动调用,返回一个引用,如下例子
1 | use std::ops::Deref; |
2. DerefMut
trait:它定义了一个叫做 deref_mut
的方法,这个方法返回一个可变的引用。当我们对一个实现了 DerefMut
trait 的类型使用 *mut
运算符时,deref_mut
方法会被自动调用,返回一个可变的引用
1 | use std::ops::DerefMut; |
From 与 Into
std::convert::From
trait 和 std::convert::Into
trait 表示类型转换,这种转换会接受一种类型的值并返回另一种类型的值。AsRef
trait 和 AsMut
trait 用于从一种类型借入另一种类型的引用,而 From
和 Into
会获取其参数的所有权,对其进行转换,然后将转换结果的所有权返回给调用者
From
和 Into
的定义是对称的:
1 | trait Into<T>: Sized { |
From 特质
From
特质用于定义一个类型如何从另一个类型转换而来。实现了 From
特质后,可以使用 from
函数来进行类型转换。这个特质通常用于定义清晰的转换逻辑,定义了一个结构体 Number
,并实现了从 i32
到 Number
的转换
1 | use std::convert::From; |
Into 特质
Into
特质是 From
的互补特质。如果为类型 A
实现了 From<B>
,那么同时也自动为 B
实现了 Into<A>
。这意味着你可以使用 into
方法将类型 B
转换为类型 A
。如,使用 from
和 into
方法来进行类型转换
1 | fn main() { |
使用场景
- 当你需要一个明确的转换方法时,使用
From
- 当你需要为多种类型提供灵活的转换方式时,使用
Into
这两个特质在 Rust 的错误处理中尤其常见,例如将各种错误类型转换为统一的错误类型,使得错误处理更加统一和方便
注意!
From
和 Into
是不会失败的trait——它们的 API 要求这种转换不会失败。许多转换远比这复杂得多。例如,像 i64
这样的大整数可以存储比 i32
大得多的数值,如果没有一些额外的信息,那么将像 2_000_000_000_000i64
这样的数值转换成 i32
就没有多大意义。如果进行简单的按位转换,那么其中前 32 位就会被丢弃,通常不会产生我们预期的结果
1 | let huge = 2_000_000_000_000i64; |
有很多选项可以处理这种情况。根据上下文的不同,“回绕型”转换可能比较合适。另外,像数字信号处理和控制系统这样的应用程序通常会使用“饱和型”转换,它会把比可能的最大值还要大的数值限制为最大值
TryFrom 与 TryInto
由于转换的行为方式不够清晰,因此 Rust 没有为 i32
实现 From<i64>
,也没有实现任何其他可能丢失信息的数值类型之间的转换,而是为 i32
实现了 TryFrom<i64>
。TryFrom
和 TryInto
是 From
和 Into
的容错版“表亲”,这种转换同样是双向的,实现了 TryFrom
也就意味着实现了 TryInto
TryFrom
和 TryInto
的定义比 From
和 Into
稍微复杂一点儿
1 | pub trait TryFrom<T>: Sized { |
TryFrom 特质
TryFrom
特质用于定义一个可能失败的类型转换。如果转换可能因为某些原因失败(例如,超出范围、格式错误等),则使用 TryFrom
。它返回一个 Result 类型,成功时包含目标类型,失败时包含错误信息。如这个例子中,SmallNumber
只接受 0 到 255 范围内的 i32
值。如果尝试从超出此范围的 i32
值转换,则会返回错误
1 | use std::convert::TryFrom; |
TryInto 特质
TryInto
特质是 TryFrom
的互补特质。如果为类型 A
实现了 TryFrom<B>
,那么同时也自动为 B
实现了 TryInto<A>
。这意味着你可以使用 try_into
方法尝试将类型 B
转换为类型 A
,并处理可能的错误。
在这个例子中,我们展示了如何使用 TryFrom
和 TryInto
来处理可能失败的转换
1 | fn main() { |
使用场景
- 当输入数据的有效性不确定时,使用
TryFrom
和TryInto
可以安全地尝试进行类型转换 - 它们常用于处理外部数据,如用户输入、文件读取等,这些数据可能不满足我们的预期格式或范围
这两个特质提供了一种类型安全的方式来处理可能错误的转换,使得代码更加健壮和易于维护
From
和 Into
可以将类型与简单转换关联起来,而 TryFrom
和 TryInto
通过 Result
提供的富有表现力的错误处理扩展了 From
和 Into
的简单转换。这 4 个trait可以一起使用,在同一个 crate 中关联多个类型
小结
语言扩展 trait 已经了解了,里面有很多新的概念,虽然敲了示例代码,距离熟练掌握还有很长的路要走,还需多敲代码,在实践中夯实基础
欢迎大家讨论交流,如果喜欢本文章或感觉文章有用,动动你那发财的小手点赞、收藏、关注再走呗
^_^
微信公众号:草帽Lufei
掘金:草帽lufei