附录 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 特征。如果相等断言失败,此宏将打印作为参数给出的实例的值,以便程序员可以看到为什么两个实例不相等。


用于相等比较的 PartialEqEq


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 是否相同。


用于排序比较的 PartialOrdOrd


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 应用于同时实现 PartialOrdEq 的类型(而 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> 中。