# 附录 C：派生特质

在这本书的不同地方，我们都讨论了 `derive` 属性，咱们可以用于结构体或枚举的定义。`derive` 属性将生成代码，对咱们以 `derive` 语法注解的类型，以某个特质的默认实现该特质。

> **译注**：我们在第 20 章 [自定义 `derive` 宏](../advanced_features/macros.md#自定义-derive-宏) 小节中讨论过 `derive` 宏的原理。他们通常会用到 `syn` 和 `quote` 代码箱，按照 Rust 语法树规则操作源代码从而在编译时生成代码，提供了 Rust 中的元编程能力。

在这个附录中，我们提供了对标准库中，所有可与  `derive` 一起使用的特质的参考。每个小节涵盖都会介绍：

- 派生这个特质将启用哪些运算符和方法；
- `derive` 会执行该特质所提供的何种实现；
- 实现该特质对类型意味着什么；
- 允许及不允许咱们实现该特质的条件；
- 需要该特质的操作示例。

当咱们想要与 `derive` 属性提供的不同行为时，请查阅每种特质的 [标准库文档](https://doc.rust-lang.org/std/index.html)，了解有关如何手动实现他们的详细信息。

这里列出的特质是一些仅有的由标准库定义、可使用 `derive` 对咱们的类型实现的特质。其他定义在标准库中的特质没有合理的默认行为，因此咱们需要根据具体需求，以合理的方式自行实现他们。

无法派生的特质的一个示例是 `Display`，他为最终用户处理格式化。咱们应该始终考虑向最终用户展示类型时的适当方式。最终用户应该被允许看类型的哪些部分？他们会发现哪些部分是相关的？哪种数据格式对他们来说最相关？Rust 编译器没有这种洞察力，因此无法为咱们提供适当的默认行为。

这个附录中提供的派生特质列表并不全面：库可以针对自己的特质实现 `derive`，构造咱们可以真正开放式地与 `derive` 一起使用的特质列表。实现 `derive` 涉及使用过程宏，这在第 20 章的 [自定义 `derive` 宏](../advanced_features/macros.md#自定义-derive-宏) 小节中介绍过。


## 用于程序员输出的 `Debug`

`Debug` 特质支持以格式字符串形式的调试输出，咱们可以通过在 `{}` 占位符内添加 `:?` 表示。

`Debug` 特质允许咱们出于调试目的，打印某种类型的实例，这样咱们和该类型的其他程序员就可以在程序执行的特定点检查该实例。

例如，在使用 `assert_eq!` 宏时，`Debug` 特质就是必需的。当相等断言失败时，该宏会打印作为参数给出的示例的值，以便程序员可以了解为何两个实例不相等。


## 用于相等比较的 `PartialEq` 和 `Eq`

`PartialEq` 特质允许咱们比较某种类型的两个实例以检查是否相等，并支持使用 `==` 与 `!=` 运算符。

派生 `PartialEq` 会实现 `eq` 方法。当对结构体派生 `ParitalEq` 时，只有 *所有* 字段都相等时，两个实例才相等，若 *任何* 字段不等，则这两个实例不相等。当对枚举派生时，每个变种与自身相等，并且不等于其他变种。

例如，在使用 `assert_eq!` 宏下，`PartialEq` 特质就是必需的，因为该宏需要能够比较两个类型实例的相等性。

`Eq` 特质没有方法。其目的是表明，对于其注解的类型的每个值，该值都等于其自身。`Eq` 特质只能应用于同时实现 `PartialEq` 的类型，尽管并非所有实现 `PartialEq` 的类型都可以实现 `Eq`。浮点数类型就是一个示例：浮点数的实现规定，两个非数字，the not-a-number, `NaN`，值的实例彼此不相等。

需要 `Eq` 的一个示例是 `HashMap<K, V>` 中的键，这样 `HashMap<K, V>` 才能判断两个键是否相同。


## 用于排序比较的 `PartialOrd` 和 `Ord`

`PartialOrd` 特质允许咱们出于排序目的比较某种类型的实例。实现 `PartialOrd` 的类型可以与 `<`、`>`、`<=` 及 `>=` 等运算符一起使用。咱们只能对那些同时实现 `PartialEq` 的类型应用 `PartialOrd` 特质。

派生 `PartialOrd` 会实现 `partial_cmp` 方法，其返回一个 `Option<Ordering>` 类型，当所给的值未产生顺序时其将为 `None`。 无法产生顺序的一个示例是 `NaN` 的浮点数值，尽管该类型的大多数值都可以比较。对任意浮点数和 `NaN` 调用 `partial_cmp` 都将返回 `None`。

在对结构体派生时，`PartialOrd` 会通过字段在结构体定义中出现的顺序，比较每个字段中的值，来比较两个实例。当对枚举派生时，枚举定义中较早声明的枚举变种，被视为小于较晚列出的变种。

例如，对于 `rand` 代码箱中的 `gen_range` 方法，`PartialOrd` 便是必需的，该方法会生成一个由范围表达式指定的范围中的随机值。

`Ord` 特质让咱们知道对于其注解类型的任意两个值，是否存在有效的顺序。`Ord` 特质实现 `cmp` 方法，该方法返回一个 `Ordering` 类型，而非 `Option<Ordering>`，因为有效的排序始终是可能的。咱们只能对同时实现 `PartialOrd` 及 `Eq` (且 `Eq` 需要 `PartialEq`) 的类型应用 `Ord` 特质。当对结构体和枚举派生时，`cmp` 的行为方式与 `PartialOrd` 中的 `partial_cmp` 的派生实现相同。

需要 `Ord` 的一个示例是在 `BTreeSet<T>` 中存储值时，这是一种根据值的排序顺序存储数据的数据结构。


## 用于复制值的 `Clone` 和 `Copy`

`Clone` 特质允许咱们显式地创建值的深拷贝，而复制过程可能涉及运行任意代码和拷贝堆数据。有关 `Clone` 的更多信息，请参阅第 4 章中 [变量与数据相互作用：克隆](../ownership/about_ownership.md#变量与数据相互作用克隆) 小节。

派生 `Clone` 会实现 `clone` 方法，当该方法针对整个类型实现时，会对该类型的每个部分调用 `clone`。这意味着类型中的所有字段或这，也必须实现 `Clone` 才能派生 `Clone`。

需要 `Clone` 的一个示例是对切片调用 `to_vec` 方法。切片并不拥有其包含的类型实例，但 `to_vec` 返回的矢量值需要拥有其实例，因此 `to_vec` 会对每个项目调用 `clone`。因此，切片中存储的类型必须实现 `Clone`。

`Copy` 特质允许咱们仅通过拷贝存储在栈上的二进制位来复制值；无需任意代码。有关 `Copy` 的更多信息，请参阅第 4 章中 [唯栈数据：拷贝](../ownership/about_ownership.md#唯栈数据拷贝) 小节。

`Copy` 特质未定义任何方法，以防止程序员重载这些方法，从而避免违反 “不运行任意代码” 这一假设。这样一来，所有程序员都可以假设拷贝值会非常快。

`Copy` 特质很少需要；实现 `Copy` 的类型具有可用的优化，这意味着咱们不必调用 `clone`，这使代码更加简洁。

通过 `Copy` 能实现的操作，咱们都可以通过 `Clone` 完成，但代码可能会更慢，或者必须在某些地方使用 `clone`。


## 用于映射值到固定大小值的 `Hash`

`Hash` 特质允许咱们取任意大小的类型的示例，并使用哈希函数映射该示例到固定大小的值。派生 `Hash` 会实现 `hash` 方法。`hash` 方法的派生实现，会组合对该类型的每个部分调用 `hash` 的结果，这就意味所有字段或这也必须实现 `Hash` 才能派生 `Hash`。

需要 `Hash` 的一个示例是，在 `Hash<K, V>` 中存储键以高效地存储数据。


## 用于默认值的 `Default`

`Default` 特质允许咱们创建某种类型的默认值。派生 `Default` 会实现 `default` 函数。`default` 函数的派生实现会对类型的各个部分调用 `default` 函数，这意味类型中的所有字段或者也必须实现 `Default`，才能派生 `Defualt`。

`Default::default` 函数通常与第 5 章中 [通过结构体更新语法创建实例](/structs/defining_and_instantiating.md#通过结构体更新语法创建实例) 小节中讨论的结构体更新语法结合使用。咱们可以自定义结构体的几个字段，然后使用 `..Default::default()`，为其余字段设置并使用默认值。

例如，当咱们对 `Option<T>` 实例使用 `unwrap_or_default` 方法时，`Default` 特质就是必需的。当 `Option<T>` 为 `None` 时，方法 `unwrap_or_default` 将返回 `Defualt::default` 针对存储在 `Option<T>` 中的类型 `T` 的结果。


