|
|
|
@ -1 +1,92 @@
|
|
|
|
|
# C - 可导出的 trait
|
|
|
|
|
# 附录C - 可派生的 trait
|
|
|
|
|
|
|
|
|
|
> [appendix-03-derivable-traits.md][appendix-03]
|
|
|
|
|
><br />
|
|
|
|
|
> commit 32215c1d96c9046c0b553a05fa5ec3ede2e125c3
|
|
|
|
|
|
|
|
|
|
[appendix-03]: https://github.com/rust-lang/book/blob/master/2018-edition/src/appendix-03-derivable-traits.md
|
|
|
|
|
|
|
|
|
|
在本书的各个部分中,我们讨论了可应用于结构体和枚举的 `derive` 属性。`derive` 属性生成的代码在使用 `derive` 语法注释的类型之上实现了带有默认实现的 trait 。
|
|
|
|
|
|
|
|
|
|
在该附录中,我们提供标准库中所有可以使用 `derive` 的 trait 的参考。每部分都包含:
|
|
|
|
|
|
|
|
|
|
* 该 trait 将会派生什么样的操作符和方法
|
|
|
|
|
* 由 `derive` 提供什么样的 trait 实现
|
|
|
|
|
* 由什么来实现类型的 trait
|
|
|
|
|
* What implementing the trait signifies about the type
|
|
|
|
|
* 是否允许实现该 trait 的条件
|
|
|
|
|
* 需要 trait 操作的例子
|
|
|
|
|
|
|
|
|
|
与 `derive` 属性提供的行为相比如果你需要与之不同的行为,请查阅标准库文档以获取每个 trait 的详情,来手动实现它们。
|
|
|
|
|
|
|
|
|
|
在类型上无法使用 `derive` 实现标准库的其余 trait。这些 trait 没有合理的默认行为, 因此,你可以以一种尝试完成的合理方式实现它们。
|
|
|
|
|
|
|
|
|
|
一个无法被派生的 trait 的例子是为最终用户处理格式化的 `Display` 。你应该时常考虑使用合适的方法来为最终用户显示一个类型。最终用户应该看到类型的什么部分?他们会找出相关部分吗?对他们来说,最相互关联的数据格式是什么样的?Rust 编译器没有这样的洞察力,因此,无法为你提供合适的默认行为。
|
|
|
|
|
|
|
|
|
|
本附录所提供的可派生 trait 列表并不全面:库可以为它们自己的 trait 实现 `derive` , 让可以使用 `derive` 的 trait 列表真诚的开放。实现 `derive` 涉及使用程序化宏,这在附录D中有介绍。
|
|
|
|
|
|
|
|
|
|
## 编程人员输出的 `Debug`
|
|
|
|
|
|
|
|
|
|
`Debug` trait 在格式化字符串中使调试格式化,你可以在 `{}` 占位符里面加上 `:?` 显示它。
|
|
|
|
|
|
|
|
|
|
`Debug` trait 允许你以调试目的来打印一个类型的实例,因此,使用类型的你以及其他的编程人员可以让程序在执行时在指定点上显示一个实例。
|
|
|
|
|
|
|
|
|
|
例如,在使用 `assert_eq!` 宏时,Debug` trait 是必须的。如果等式断言失败,这个宏就把给定实例的值作为参数打印出来,因此,编程人员可以看到两个实例为什么不相等。
|
|
|
|
|
|
|
|
|
|
## 等值比较的 `PartialEq` 和 `Eq`
|
|
|
|
|
|
|
|
|
|
`PartialEq` trait 可以比较一个类型的实例以检查是否相等,并且可以使用 `==` 和 `!=` 操作符。
|
|
|
|
|
|
|
|
|
|
派生的 `PartialEq` 实现了 `eq` 方法。当 `PartialEq` 在结构体上派生时,只有*所有*的字段都相等时两个实例才相等,同时只要有字段不相等则两个实例就不相等。当在枚举上派生时,每一个变体(variant)都和它自身相等,且和其他变体都不相等。
|
|
|
|
|
|
|
|
|
|
例如,当使用 `assert_eq!` 宏时,需要比较比较一个类型的两个实例是否相等,则 `PartialEq` trait 是必须的。
|
|
|
|
|
|
|
|
|
|
`Eq` trait 没有方法。其目的是为每一个注解类型的值作标志,其值等于其自身。 `Eq` trait 只能应用于那些实现了 `PartialEq` 的类型,但并非所有实现了 `PartialEq` 的类型可以实现 `Eq`。浮点类型就是一个例子:浮点数状态的实现,两个非数字(`NaN`,not-a-number)值是互不相等的。
|
|
|
|
|
|
|
|
|
|
例如,对于一个 `HashMap<K, V>` 中的 key 来说, `Eq` 是必须的,这样 `HashMap<K, V>` 就可以知道两个 key 是否一样了。
|
|
|
|
|
|
|
|
|
|
## 次序比较的 `PartialOrd` 和 `Ord`
|
|
|
|
|
|
|
|
|
|
`PartialOrd` trait 可以基于排序的目的而比较一个类型的实例。实现了 `PartialOrd` 的类型可以使用 `<`、 `>`、`<=` 和 `>=` 操作符。但只能在同时实现了 `PartialEq` 的类型上使用 `PartialOrd`。
|
|
|
|
|
|
|
|
|
|
派生 `PartialOrd` 实现了 `partial_cmp` 方法,其返回一个 `Option<Ordering>` ,但当给定值无法产生顺序时将返回 `None`。尽管大多数类型的值都可以比较,但一个无法产生顺序的例子是:浮点类型的非数字值(`NaN`,not-a-number)。当在浮点数上调用 `partial_cmp` 时,`NaN` 的浮点数将返回 `None`。
|
|
|
|
|
|
|
|
|
|
当在结构体上派生时,`PartialOrd` 以在结构体定义中字段出现的顺序比较每个字段的值来比较两个实例。当在枚举上派生时,认为在枚举定义中声明较早的枚举变体小于其后的变体。
|
|
|
|
|
|
|
|
|
|
例如,对于来自于 `rand` carte 中的 `gen_range` 方法来说,当在一个大值和小值指定的范围内生成一个随机值时,`PartialOrd` trait 是必须的。
|
|
|
|
|
|
|
|
|
|
`Ord` trait 也让你明白在一个带注解类型上的任意两个值存在有效顺序。`Ord` trait 实现了 `cmp` 方法,它返回一个 `Ordering` 而不是 `Option<Ordering>`,因为总存在一个合法的顺序。只可以在实现了 `PartialOrd` 和 `Eq`( `Eq` 依赖 `PartialEq` )的类型上使用 `Ord` trait 。当在结构体或枚举上派生时, `cmp` 和以 `PartialOrd` 派生实现的 `partial_cmp` 表现一致。
|
|
|
|
|
|
|
|
|
|
例如,当在 `BTreeSet<T>` (一种基于有序值存储数据的数据结构)上存值时,`Ord` 是必须的。
|
|
|
|
|
|
|
|
|
|
## 复制值的 `Clone` 和 `Copy`
|
|
|
|
|
|
|
|
|
|
`Clone` trait 可以明确地创建一个值的深拷贝( deep copy ),复制过程可能包含任意代码的执行以及堆上数据的复制。查阅第四章“变量和数据的交互方式:移动”以获取有关 `Clone` 的更多信息。
|
|
|
|
|
|
|
|
|
|
派生 `Clone` 实现了 `clone` 方法,其为整个的类型实现时,在类型的每一部分上调用了 `clone` 方法。这意味着类型中所有字段或值也必须实现 `Clone` 来派生 `Clone` 。
|
|
|
|
|
|
|
|
|
|
例如,当在一个切片上调用 `to_vec` 方法时,`Clone` 是必须的。切片并不拥有其所包含实例的类型,但是从 `to_vec` 中返回的 vector 需要拥有其实例,因此,`to_vec` 在每个元素上调用 `clone`。因此,存储在切片中的类型必须实现 `Clone`。
|
|
|
|
|
|
|
|
|
|
`Copy` trait 允许你通过只拷贝存储在栈上的位来复制值而不需要其他代码。查阅第四章“只在栈上的数据:拷贝”的部分来获取有关 `Copy` 的更多信息。
|
|
|
|
|
|
|
|
|
|
`Copy` trait 并未定义任何方法来阻止编程人员重写这些方法或违反无代码可执行的假设。所以,所有的编程人员可以假设复制一个值非常快。
|
|
|
|
|
|
|
|
|
|
可以在类型内部全部实现 `Copy` trait 的任意类型上派生 `Copy`。 但只可以在那些同时实现了 `Clone` 的类型上使用 `Copy` trait ,因为一个实现 `Copy` 的类型在尝试实现 `Clone` 时执行和 `Copy` 相同的任务。
|
|
|
|
|
|
|
|
|
|
`Copy` trait 很少使用;实现 `Copy` 的类型是可以优化的,这意味着你无需调用 `clone`,这让代码更简洁。
|
|
|
|
|
|
|
|
|
|
使用 `Clone` 实现 `Copy` 也是有可能的,但代码可能会稍慢或是要使用 `clone` 替代。
|
|
|
|
|
|
|
|
|
|
## 固定大小的值到值映射的 `Hash`
|
|
|
|
|
|
|
|
|
|
`Hash` trait 可以实例化一个任意大小的类型,并且能够用哈希(hash)函数将该实例映射到一个固定大小的值上。派生 `Hash` 实现了 `hash` 方法。`hash` 方法的派生实现结合了在类型的每部分调用 `hash` 的结果,这意味着所有的字段或值也必须将 `Hash` 实现为派生 `Hash`。
|
|
|
|
|
|
|
|
|
|
例如,在 `HashMap<K, V>` 的 key 上存储有效数据时,`Hash` 是必须的。
|
|
|
|
|
|
|
|
|
|
## 默认值的 `Default`
|
|
|
|
|
|
|
|
|
|
`Default` trait 使你创建一个类型的默认值。 派生 `Default` 实现了 `default` 函数。`default` 函数的派生实现调用了类型每部分的 `default` 函数,这意味着类型中所有的字段或值也必须把 `Default` 实现为派生 `Default` 。
|
|
|
|
|
|
|
|
|
|
`Default::default` 函数通常结合结构体更新语法一起使用,这在第五章的“使用结构体更新语法从其他实例中创建实例”部分有讨论。可以自定义一个结构体的一小部分字段而剩余字段则使用 `..Default::default()` 设置为默认值。
|
|
|
|
|
|
|
|
|
|
例如,当你在 `Option<T>` 实例上使用 `unwrap_or_default` 方法时,`Default` trait是必须的。如果 `Option<T>` 是 `None`的话, `unwrap_or_default` 方法将返回存储在 `Option<T>` 中 `T` 类型的 `Default::default` 的结果。
|
|
|
|
|