附录 C:派生特质

Appendix C: Derivable Traits

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

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

  • 此特质将启用那些操作符与方法;

  • derive 所提供到的该特质实现会做些什么;

  • 实现该特质对那个类型意味着什么;

  • 允许及不允许实现该特质的情况;

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

若咱们想要不同于由 derive 属性所提供的行为,请参考 标准库文档,了解如何亲自实现各个特质的详细信息。

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

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

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

输出给编程者的 Debug

Debug for Programmer Output

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

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

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

用于相等比较的 PartialEqEq

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

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

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

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

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

用于排序比较的 PartialOrdOrd

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>。咱们只可对那些同时实现了 PartialOrdEq (而 Eq 要求 PartialEq) 的类型,实现这个 Ord 特质。当于结构体及枚举上派生 Ord 时,cmp 就会以与 PartialOrdpartial_cmp 的派生实现同样方式行事。

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

用于复制值的 CloneCopy

Clone and Copy for Duplicating Values

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

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

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

Copy 特质实现了只通过拷贝存储在栈上的二进制位,而复制某个值;任意代码并无必要。请参阅第 4 章中 “唯栈数据:拷贝”,了解更多有关 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 章中 “使用结构体更新语法从其他实例创建出实例” 小节里曾讨论过的结构体更新语法结合使用的。咱们可以定制结构体的几个字段,并在随后通过使用 ..Default::default(),为其余字段设置并使用默认值。

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

Last change: 2023-12-01, commit: aa11f3e