附录 C:可派生特征
在本书的不同地方,我们讨论了 derive
属性,你可以将其应用于结构或枚举定义。derive
属性生成的代码将在您使用 derive
语法注释的类型上使用自己的默认实现实现特征。
在本附录中,我们提供了 standard 库中可与 derive
一起使用的所有特征的参考。每个部分都涵盖:
派生此 trait 的运算符和方法将启用derive
提供的 trait 的实现有什么作用
实现 trait 对类型意味着什么
允许或不允许您实施 trait 的条件
需要 trait 的作示例
如果希望行为与 derive
属性提供的行为不同,请查阅标准库文档
,以获取有关如何手动实施它们的详细信息。
此处列出的这些 trait 是唯一由标准库定义的 trait,可以使用 derive
在你的类型上实现。标准库中定义的其他 trait 没有合理的默认行为,因此您需要以对要完成的任务有意义的方式实现它们。
无法派生的特征的一个示例是 Display
,它为最终用户处理格式设置。您应该始终考虑向最终用户显示类型的适当方式。应允许最终用户查看类型的哪些部分?他们认为哪些部分相关?哪种格式的数据与他们最相关?Rust 编译器没有这种洞察力,因此它无法为您提供适当的默认行为。
本附录中提供的可派生特征列表并不全面:库可以实施 derive
针对自己的特征,从而使您可以使用的特征列表真正开放式。实现
derive
涉及使用过程宏,这在
第 19 章的 “宏” 部分。
Debug
for Programmer 输出
Debug
trait 在格式字符串中启用调试格式,您可以通过在 {}
占位符中添加 :?
来指示这一点。
Debug
trait 允许您打印某个类型的实例以进行调试,以便您和其他使用您类型的程序员可以在程序执行的特定点检查实例。
例如,在使用 assert_eq!
宏时,需要 Debug
特征。如果相等断言失败,此宏将打印作为参数给出的实例的值,以便程序员可以看到为什么两个实例不相等。
用于相等比较的 PartialEq
和 Eq
PartialEq
trait 允许您比较类型的实例以检查是否相等,并允许使用 ==
和 !=
运算符。
派生 PartialEq
实现 eq
方法。在结构上派生 PartialEq
时,仅当所有字段都相等时,两个实例才相等,如果任何字段不相等,则实例不相等。当在 enum 上派生时,每个变体都等于自身,不等于其他变体。
PartialEq
特征是必需的,例如,使用
assert_eq!
宏,它需要能够比较同一类型的两个实例是否相等。
Eq
trait 没有方法。其目的是表示对于 annotated 类型的每个值,该值等于自身。Eq
trait 只能应用于也实现 PartialEq
的类型,尽管并非所有实现 PartialEq
的类型都可以实现 Eq
。这方面的一个例子是浮点数类型:浮点数的实现指出非数字 (NaN
) 值的两个实例彼此不相等。
需要 Eq
的一个示例是 HashMap<K、V>
中的键,因此
HashMap<K, V>
可以判断两个 key 是否相同。
用于排序比较的 PartialOrd
和 Ord
PartialOrd
trait 允许您比较类型的实例以进行排序。实现 PartialOrd
的类型可以与 <
、>
、
<=
和 >=
运算符。你只能将 PartialOrd
trait 应用于也实现 PartialEq
的类型。
派生 PartialOrd
实现 partial_cmp
方法,该方法返回一个
选项<rdering>
,当给定的值不产生排序时,它将为 None
。即使可以比较该类型的大多数值,也不会产生排序的值的一个示例是非数字 (NaN
) 浮点值。使用任意浮点数和 NaN
调用 partial_cmp
浮点值将返回 None
。
在结构上派生时,PartialOrd
通过按字段在结构定义中出现的顺序比较每个字段中的值来比较两个实例。当在枚举上派生时,枚举定义中前面声明的枚举的变体被认为小于后面列出的变体。
PartialOrd
特征是必需的,例如,对于 rand
crate 中的 gen_range
方法,该方法在 range 表达式指定的范围内生成随机值。
Ord
trait 允许您知道,对于 annotated 类型的任意两个值,都将存在有效的 ordering。Ord
trait 实现了 cmp
方法,该方法返回 Ordering
而不是 Option<Ordering>
,因为始终可以进行有效的排序。你只能将 Ord
trait 应用于同时实现 PartialOrd
和 Eq
的类型(而 Eq
需要 PartialEq
)。在结构和枚举上派生时,cmp
的行为方式与 partial_cmp
的派生实现与 PartialOrd
的行为相同。
需要 Ord
的一个示例是在 BTreeSet<T> 中存储值时,BTreeSet<T>
是一种根据值的排序顺序存储数据的数据结构。
克隆
和复制
以复制值
Clone
trait 允许您显式创建值的深层副本,并且复制过程可能涉及运行任意代码和复制堆数据。请参阅“变量和数据交互的方式:
Clone“部分,了解有关 Clone
的更多信息。
派生 Clone
实现 clone
方法,当为整个类型实现时,该方法会在类型的每个部分上调用 clone
。这意味着类型中的所有字段或值也必须实现 Clone
才能派生 Clone
。
需要 Clone
的一个示例是在切片上调用 to_vec
方法时。该 slice 不拥有它包含的类型实例,但从 to_vec
返回的 vector 需要拥有其实例,因此 to_vec
调用
clone
在每个项目上。因此,存储在 slice 中的类型必须实现 Clone
。
的 Copy
trait 允许您通过仅复制存储在堆栈上的位来复制值;不需要任意代码。请参阅“仅堆栈数据:
Copy“部分,了解有关 Copy
的更多信息。
Copy
trait 没有定义任何方法来防止程序员重载这些方法并违反没有运行任意代码的假设。这样,所有程序员都可以假设复制一个值会非常快。
可以在其部分全部实现 Copy
的任何类型上派生 Copy
。实现 Copy
的类型也必须实现 Clone
,因为实现
Copy
有一个 Clone
的普通实现,它执行与
复制
。
很少需要 Copy
特征;实现 Copy
的类型具有可用的优化,这意味着您不必调用 clone
,这会使代码更加简洁。
使用 Copy
的所有可能作也可以通过 Clone
完成,但代码可能会更慢或必须在某些地方使用 clone
。
用于将值映射到固定大小值的哈希
Hash
trait 允许您获取任意大小的 type 实例,并且
使用哈希函数将该实例映射到 fixed size 的值。推导
Hash
实现 hash
方法。哈希
的派生实现
方法将对类型的每个部分调用 hash
的结果组合在一起,这意味着所有字段或值也必须实现 Hash
才能派生 Hash
。
需要 Hash
的一个示例是将键存储在 HashMap<K、V>
中
以有效地存储数据。
默认值
的 Default
trait 允许您为类型创建默认值。推导
default
实现 default
函数。的
default
函数在类型的每个部分上调用 default
函数,这意味着该类型中的所有字段或值也必须实现 Default
才能派生 Default
。
Default::d efault
函数通常与“从其他实例创建实例”中讨论的 struct update 语法结合使用
结构体更新
语法”
部分。您可以自定义结构体的几个字段,然后
使用
..默认值::d efault()
。
的 Default
特征在您使用方法时是必需的 unwrap_or_default
on
例如,Option<T>
实例。如果选项<T>
为 None
,则方法
unwrap_or_default
将返回 Default::d efault
类型的结果
T
存储在选项<T>
中。