# 附录 C：派生特质

**Appendix C: Derivable Traits**


本书的多个不同地方，咱们都曾讨论过 `derive` 属性，咱们可将其应用到结构体或枚举定义。`derive` 属性会在咱们以 `derive` 语法注解的类型上，生成将以某个特质自身默认实现，而实现该特质的代码。

在这个附录中，咱们会提供到标准库中，咱们可以与 `derive` 一起使用的全部特质的参考。以下各个小节均会讲到：

- 此特质将启用那些操作符与方法；

- 由 `derive` 所提供到的该特质实现会做些什么；

- 实现该特质对那个类型意味着什么；

- 允许及不允许实现该特质的情况；

- 需要该特质操作的示例。



若咱们想要不同于由 `derive` 属性所提供的行为，请参考 [标准库文档](https://doc.rust-lang.org/std/index.html)，了解如何亲自实现各个特质的详细信息。

这里列出的这些特质，只是一些由标准库所提供的，可使用 `derive` 实现于咱们类型上的那些。定义在标准库中别的一些特质，则没有什么合理的默认行为，因此是否要以对于咱们正尝试完成的东西有意义的方式，实现他们就取决于咱们自己了。

不能派生的一个特质示例便是 `Display`，其为终端用户处理格式化。咱们应始终要考虑将某个类型显示给用户的恰当方式。终端用户应被允许看到该类型的哪些部分？他们会发现哪些部分是相关的？数据的何种形式才是与他们最为密切相关的？Rust 编译器并无这种见解，因此他就无法为咱们提供到恰当的默认行为。

这个附录中所提供到的派生特质清单并不详尽：库可以为他们自己的特质实现 `derive`，从而领导咱们可使用 `derive` 的特质清单为真正开放的。实现 `derive` 设计到使用程序性宏，这在第 19 章的 [“关于宏”](Ch19_Advanced_Features.md#关于宏) 小节讲到过。


## 输出给编程者的 `Debug`

**`Debug` for Programmer Output**


`Debug` 特质实现了格式字符串中的格式化，所谓格式字符串，即咱们通过在 `{}` 里添加 `:?` 所表示的。

`Debug` 特质允许咱们为调试目的打印某种类型的实例，如此咱们以及用到咱们类型的其他编程者，就可以在程序执行的某个特定时刻，就其某个实例加以探查。

在比如用到 `assert_eq!` 宏中等情况下，`Debug` 特质便是要求使用的。`assert_eq!` 这个宏在相等断言失败时，就会打印出作为参数所给到的两个实例值，如此编程者就可以看到为何这两个实例不相等。


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


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

对 `PartialEq` 进行派生，就会实现 `eq` 方法。当 `ParitalEq` 实在结构体上实现的时，只有在两个实例的 *全部* 字段都相等时，他们才是相等的，且在有任何字段不等时，两个实例便不相等。当在枚举上派生时，枚举的各个变种与自身相等，而不等于其他任何变种。

在使用需要能够比较某个类型的两个实例是否相等的 `assert_eq!` 宏时，就需要这个 `PartialEq` 特质。

而 `Eq` 特质则没有方法。他的目的是要表明，所注解的类型的每个值，其值都等于他自身。尽管并非所有实现 `PartialEq` 的类型都可以实现 `Eq`，但 `Eq` 特质却只可应用到那些同时实现了 `PartialEq` 的类型。这方面的一个示例，便是浮点数类型：浮点数的实现，就表明两个非数字（the not-a-number, `NaN`）的值，是各自不相等的。

要求 `Eq` 的一个示例，就是 `HashMap<K, V>` 中的那些键，如此 `HashMap<K, V>` 就可以区分出两个键是否一致。


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


**`PartialOrd` and `Ord` for Ordering Comparisons**

`PartialOrd` 特质实现为排序目的，而比较某种类型的那些实例。实现了 `PartialOrd` 的类型，便可与 `<`、`>`、`<=` 及 `>=` 符号一起使用了。咱们只能对那些同时实现了 `PartialEq` 的类型，应用这个 `PartialOrd` 特质。

派生 `PartialOrd`，会实现 `partial_cmp` 方法，该方法会返回一个在所给的那些值不会产生出顺序时，将为 `None` 的一个 `Option<Ordering>`。至于即使那种类型的大多数值都可被比较，但仍不会产生出顺序的值的一个示例，便是非数字（`NaN`）浮点值。在任何浮点数和非数字浮点值下调用 `partial_cmp`，都会返回 `None`。

在于结构体上派生时，`PartialOrd` 会通过字段出现在结构体定义中的顺序，比较每个字段中的值，比较两个实例。而当于枚举上派生时，枚举定义中较早声明的枚举变种，被当作是小于后面所列出的那些变种的。

在比如会产生出由范围表达式所指定范围中一个随机数的， `rand` 代码箱的 `gen_range` 方法来说，`PartialOrd` 特质便是需要的。

`Ord` 特质实现对所注解类型的任何两个值，将存在有效顺序的掌握。`Ord` 特质会实现 `cmp` 方法，由于有效排序将始终可行，因此该方法返回的是 `Ordering` 而非 `Option<Ordering>`。咱们只可对那些同时实现了 `PartialOrd` 及 `Eq` (而 `Eq` 要求 `PartialEq`) 的类型，实现这个 `Ord` 特质。当于结构体及枚举上派生 `Ord` 时，`cmp` 就会以与 `PartialOrd` 下 `partial_cmp` 的派生实现同样方式行事。

要求 `Ord` 的一个示例，即为将一些值存储在 `BTreeSet<T>` 这种根据值的排序，而存储数据的数据结构中时。


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

**`Clone` and `Copy` for Duplicating Values**


`Clone` 特质实现了显式创建值的深拷贝，而该复制过程则可能涉及运行一些任意代码，arbitary code，与拷贝内存堆数据。请参阅第 4 章中 [“变量与数据交互方式：克隆”](Ch04_Understanding_Ownership.md#变量与数据交互方式之二克隆) 小节，了解更多有关 `Clone` 的信息。

派生 `Clone` 会实现 `clone` 方法，当对整个类型实现了这个方法时，其就会在该类型的各个部分上调用 `clone`。这意味着类型要派生 `Clone` 其中的全部字段或值，都必须同时实现 `Clone`。

需要 `Clone` 特质的一个示例，便是在切片上调用 `to_vec` 方法时。切片不持有其包含的那些类型实例，但自 `to_vec` 所返回的那个矢量值，却将需要持有他的那些实例，从而 `to_vec` 会调用各个条目上的 `clone`。因此，存储在切片中的类型，就必须实现 `Clone`。

`Copy` 特质实现了只通过拷贝存储在栈上的二进制位，而复制某个值；任意代码并无必要。请参阅第 4 章中 [“唯栈数据：拷贝”](Ch04_Understanding_Ownership.md#唯栈数据拷贝stack-only-data-copy)，了解更多有关 `Copy` 的信息。

`Copy` 特质没有定义阻止编程者过载那些方法，及破坏不会有任意代码运行这个假设的任何方法。那样的话，所有编程者就都可以假定，拷贝值将会非常快。

咱们可在其组成部分都实现了 `Copy` 的任何类型上派生 `Copy` 特质。由于实现 `Copy` 的类型，都有着执行与 `Copy` 同样任务的一个 `Clone` 的简单实现，因此实现 `Copy` 的类型必须同时实现 `Clone`。

很少需要 `Copy` 特质；实现了 `Copy` 的类型，有着可供选择的优化方案，意味着咱们不必调用 `clone`，而调用 `clone` 会令到代码更简洁。

对于 `Copy` 下每种可能情况，咱们都可同时以 `Clone` 完成，除了代码可能更慢，或在一些地方不得不使用 `clone`。


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

**`Hash` for Mapping a Value to a Value of Fixed Size**


`Hash` 特质实现了取某种任意大小类型的实例，并通过使用散列函数，将那个实例映射到固定大小的值。派生 `Hash` 会实现 `hash` 方法。`hash` 放的派生实现，会将在该类型各个组成部分上调用 `hash` 的结果结合起来，这就意味着类型要派生 `Hash`，那么其全部字段，都必须同时实现 `Hash`。

要求 `Hash` 的一个示例，便是为了高效地存储数据，而在 `Hash<K, V>` 中存储那些键时。


## 用于默认值的 `Default`

**`Default` for Default Values**


`Default` 特质实现了为类型创建出一个默认值。派生 `Default` 会实现 `default` 函数。`default` 函数的派生实现，会在类型的各个部分上调用 `default` 函数，意味类型要派生 `Defualt`，其中的全部字段或值，都必须同时实现 `Default`。

`Default::default` 函数，通常是与第 5 章中 [“使用结构体更新语法从其他实例创建出实例”](Ch05_Using_Structs_to_Structure_Related_Data.md#使用结构体更新语法从其他实例创建出实例) 小节里曾讨论过的结构体更新语法结合使用的。咱们可以定制结构体的几个字段，并在随后通过使用 `..Default::default()`，为其余字段设置并使用默认值。

在 `Option<T>` 实例上使用 `unwrap_or_default` 方法时，便是需要 `Default` 特质的一个示例。当那个 `Option<T>` 为 `None` 时，方法 `unwrap_or_default` 就将返回存储在 `Option<T>` 中，那个类型 `T` 的 `Default::default` 结果。
