Rust 附錄 C:可派生的 trait

2023-03-22 15:17 更新
appendix-03-derivable-traits.md
commit c07dddac692848ade6c2112c8e15a7087fbbec45

在本書的各個(gè)部分中,我們討論了可應(yīng)用于結(jié)構(gòu)體和枚舉定義的 derive 屬性。derive 屬性會(huì)在使用 derive 語法標(biāo)記的類型上生成對(duì)應(yīng) trait 的默認(rèn)實(shí)現(xiàn)的代碼。

在本附錄中提供了標(biāo)準(zhǔn)庫中所有可以使用 derive 的 trait 的參考。這些部分涉及到:

  • 該 trait 將會(huì)派生什么樣的操作符和方法
  • 由 ?derive? 提供什么樣的 trait 實(shí)現(xiàn)
  • 由什么來實(shí)現(xiàn)類型的 trait
  • 是否允許實(shí)現(xiàn)該 trait 的條件
  • 需要 trait 操作的例子

如果你希望不同于 derive 屬性所提供的行為,請(qǐng)查閱 標(biāo)準(zhǔn)庫文檔 中每個(gè) trait 的細(xì)節(jié)以了解如何手動(dòng)實(shí)現(xiàn)它們。

這里列出的 trait 是僅有的在標(biāo)準(zhǔn)庫中定義且能通過 derive 在類型上實(shí)現(xiàn)。標(biāo)準(zhǔn)庫中定義的其它 trait 不能通過 derive 在類型上實(shí)現(xiàn)。這些 trait 不存在有意義的默認(rèn)行為,所以由你負(fù)責(zé)以合理的方式實(shí)現(xiàn)它們。

一個(gè)無法被派生的 trait 的例子是為終端用戶處理格式化的 Display 。你應(yīng)該時(shí)??紤]使用合適的方法來為終端用戶顯示一個(gè)類型。終端用戶應(yīng)該看到類型的什么部分?他們會(huì)找出相關(guān)部分嗎?對(duì)他們來說最相關(guān)的數(shù)據(jù)格式是什么樣的?Rust 編譯器沒有這樣的洞察力,因此無法為你提供合適的默認(rèn)行為。

本附錄所提供的可派生 trait 列表并不全面:庫可以為其自己的 trait 實(shí)現(xiàn) derive,可以使用 derive 的 trait 列表事實(shí)上是無限的。實(shí)現(xiàn) derive 涉及到過程宏的應(yīng)用,這在第十九章的 “宏” 有介紹。

用于程序員輸出的 Debug

Debug trait 用于開啟格式化字符串中的調(diào)試格式,其通過在 {} 占位符中增加 :? 表明。

Debug trait 允許以調(diào)試目的來打印一個(gè)類型的實(shí)例,所以使用該類型的程序員可以在程序執(zhí)行的特定時(shí)間點(diǎn)觀察其實(shí)例。

例如,在使用 assert_eq! 宏時(shí),Debug trait 是必須的。如果等式斷言失敗,這個(gè)宏就把給定實(shí)例的值作為參數(shù)打印出來,如此程序員可以看到兩個(gè)實(shí)例為什么不相等。

等值比較的 PartialEq 和 Eq

PartialEq trait 可以比較一個(gè)類型的實(shí)例以檢查是否相等,并開啟了 == 和 != 運(yùn)算符的功能。

派生的 PartialEq 實(shí)現(xiàn)了 eq 方法。當(dāng) PartialEq 在結(jié)構(gòu)體上派生時(shí),只有所有 的字段都相等時(shí)兩個(gè)實(shí)例才相等,同時(shí)只要有任何字段不相等則兩個(gè)實(shí)例就不相等。當(dāng)在枚舉上派生時(shí),每一個(gè)成員都和其自身相等,且和其他成員都不相等。

例如,當(dāng)使用 assert_eq! 宏時(shí),需要比較比較一個(gè)類型的兩個(gè)實(shí)例是否相等,則 PartialEq trait 是必須的。

Eq trait 沒有方法。其作用是表明每一個(gè)被標(biāo)記類型的值等于其自身。Eq trait 只能應(yīng)用于那些實(shí)現(xiàn)了 PartialEq 的類型,但并非所有實(shí)現(xiàn)了 PartialEq 的類型都可以實(shí)現(xiàn) Eq。浮點(diǎn)類型就是一個(gè)例子:浮點(diǎn)數(shù)的實(shí)現(xiàn)表明兩個(gè)非數(shù)字(NaN,not-a-number)值是互不相等的。

例如,對(duì)于一個(gè) HashMap<K, V> 中的 key 來說, Eq 是必須的,這樣 HashMap<K, V> 就可以知道兩個(gè) key 是否一樣了。

次序比較的 PartialOrd 和 Ord

PartialOrd trait 可以基于排序的目的而比較一個(gè)類型的實(shí)例。實(shí)現(xiàn)了 PartialOrd 的類型可以使用 <、 >、<= 和 >= 操作符。但只能在同時(shí)實(shí)現(xiàn)了 PartialEq 的類型上使用 PartialOrd

派生 PartialOrd 實(shí)現(xiàn)了 partial_cmp 方法,其返回一個(gè) Option<Ordering> ,但當(dāng)給定值無法產(chǎn)生順序時(shí)將返回 None。盡管大多數(shù)類型的值都可以比較,但一個(gè)無法產(chǎn)生順序的例子是:浮點(diǎn)類型的非數(shù)字值。當(dāng)在浮點(diǎn)數(shù)上調(diào)用 partial_cmp 時(shí),NaN 的浮點(diǎn)數(shù)將返回 None。

當(dāng)在結(jié)構(gòu)體上派生時(shí),PartialOrd 以在結(jié)構(gòu)體定義中字段出現(xiàn)的順序比較每個(gè)字段的值來比較兩個(gè)實(shí)例。當(dāng)在枚舉上派生時(shí),認(rèn)為在枚舉定義中聲明較早的枚舉變體小于其后的變體。

例如,對(duì)于來自于 rand crate 中的 gen_range 方法來說,當(dāng)在一個(gè)范圍表達(dá)式指定的范圍內(nèi)生成一個(gè)隨機(jī)值時(shí),PartialOrd trait 是必須的。

Ord trait 也讓你明白在一個(gè)帶注解類型上的任意兩個(gè)值存在有效順序。Ord trait 實(shí)現(xiàn)了 cmp 方法,它返回一個(gè) Ordering 而不是 Option<Ordering>,因?yàn)榭偞嬖谝粋€(gè)合法的順序。只可以在實(shí)現(xiàn)了 PartialOrd 和 EqEq 依賴 PartialEq)的類型上使用 Ord trait 。當(dāng)在結(jié)構(gòu)體或枚舉上派生時(shí), cmp 和以 PartialOrd 派生實(shí)現(xiàn)的 partial_cmp 表現(xiàn)一致。

例如,當(dāng)在 BTreeSet<T>(一種基于有序值存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu))上存值時(shí),Ord 是必須的。

復(fù)制值的 Clone 和 Copy

Clone trait 可以明確地創(chuàng)建一個(gè)值的深拷貝(deep copy),復(fù)制過程可能包含任意代碼的執(zhí)行以及堆上數(shù)據(jù)的復(fù)制。查閱第四章 “變量與數(shù)據(jù)交互的方式(二):克隆” 以獲取有關(guān) Clone 的更多信息。

派生 Clone 實(shí)現(xiàn)了 clone 方法,其為整個(gè)的類型實(shí)現(xiàn)時(shí),在類型的每一部分上調(diào)用了 clone 方法。這意味著類型中所有字段或值也必須實(shí)現(xiàn)了 Clone,這樣才能夠派生 Clone 。

例如,當(dāng)在一個(gè)切片(slice)上調(diào)用 to_vec 方法時(shí),Clone 是必須的。切片并不擁有其所包含實(shí)例的類型,但是從 to_vec 中返回的 vector 需要擁有其實(shí)例,因此,to_vec 在每個(gè)元素上調(diào)用 clone。因此,存儲(chǔ)在切片中的類型必須實(shí)現(xiàn) Clone。

Copy trait 允許你通過只拷貝存儲(chǔ)在棧上的位來復(fù)制值而不需要額外的代碼。查閱第四章 “只在棧上的數(shù)據(jù):拷貝” 的部分來獲取有關(guān) Copy 的更多信息。

Copy trait 并未定義任何方法來阻止編程人員重寫這些方法或違反不需要執(zhí)行額外代碼的假設(shè)。盡管如此,所有的編程人員可以假設(shè)復(fù)制(copy)一個(gè)值非常快。

可以在類型內(nèi)部全部實(shí)現(xiàn) Copy trait 的任意類型上派生 Copy。一個(gè)實(shí)現(xiàn)了 Copy 的類型必須也實(shí)現(xiàn)了 Clone,因?yàn)橐粋€(gè)實(shí)現(xiàn)了 Copy 的類型也簡單地實(shí)現(xiàn)了 Clone,其執(zhí)行和 Copy 相同的任務(wù)。

Copy trait 很少使用;實(shí)現(xiàn) Copy 的類型是可以優(yōu)化的,這意味著你無需調(diào)用 clone,這讓代碼更簡潔。

任何使用 Copy 的代碼都可以通過 Clone 實(shí)現(xiàn),但代碼可能會(huì)稍慢,或者不得不在代碼中的許多位置上使用 clone

固定大小的值到值映射的 Hash

Hash trait 可以實(shí)例化一個(gè)任意大小的類型,并且能夠用哈希(hash)函數(shù)將該實(shí)例映射到一個(gè)固定大小的值上。派生 Hash 實(shí)現(xiàn)了 hash 方法。hash 方法的派生實(shí)現(xiàn)結(jié)合了在類型的每部分調(diào)用 hash 的結(jié)果,這意味著所有的字段或值也必須實(shí)現(xiàn)了 Hash,這樣才能夠派生 Hash

例如,在 HashMap<K, V> 上存儲(chǔ)數(shù)據(jù),存放 key 的時(shí)候,Hash 是必須的。

默認(rèn)值的 Default

Default trait 使你創(chuàng)建一個(gè)類型的默認(rèn)值。 派生 Default 實(shí)現(xiàn)了 default 函數(shù)。default 函數(shù)的派生實(shí)現(xiàn)調(diào)用了類型每部分的 default 函數(shù),這意味著類型中所有的字段或值也必須實(shí)現(xiàn)了 Default,這樣才能夠派生 Default 。

Default::default 函數(shù)通常結(jié)合結(jié)構(gòu)體更新語法一起使用,這在第五章的 “使用結(jié)構(gòu)體更新語法從其他實(shí)例中創(chuàng)建實(shí)例” 部分有討論??梢宰远x一個(gè)結(jié)構(gòu)體的一小部分字段而剩余字段則使用 ..Default::default() 設(shè)置為默認(rèn)值。

例如,當(dāng)你在 Option<T> 實(shí)例上使用 unwrap_or_default 方法時(shí),Default trait 是必須的。如果 Option<T> 是 None的話, unwrap_or_default 方法將返回存儲(chǔ)在 Option<T> 中 T 類型的 Default::default 的結(jié)果。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)