ch19-03-advanced-traits.md
commit 81d05c9a6d06d79f2a85c8ea184f41dc82532d98
第十章 “trait:定義共享的行為” 部分,我們第一次涉及到了 trait,不過就像生命周期一樣,我們并沒有覆蓋一些較為高級(jí)的細(xì)節(jié)?,F(xiàn)在我們更加了解 Rust 了,可以深入理解其本質(zhì)了。
關(guān)聯(lián)類型(associated types)是一個(gè)將類型占位符與 trait 相關(guān)聯(lián)的方式,這樣 trait 的方法簽名中就可以使用這些占位符類型。trait 的實(shí)現(xiàn)者會(huì)針對(duì)特定的實(shí)現(xiàn)在這個(gè)類型的位置指定相應(yīng)的具體類型。如此可以定義一個(gè)使用多種類型的 trait,直到實(shí)現(xiàn)此 trait 時(shí)都無需知道這些類型具體是什么。
本章所描述的大部分內(nèi)容都非常少見。關(guān)聯(lián)類型則比較適中;它們比本書其他的內(nèi)容要少見,不過比本章中的很多內(nèi)容要更常見。
一個(gè)帶有關(guān)聯(lián)類型的 trait 的例子是標(biāo)準(zhǔn)庫提供的 Iterator
trait。它有一個(gè)叫做 Item
的關(guān)聯(lián)類型來替代遍歷的值的類型。第十三章的 “Iterator trait 和 next 方法” 部分曾提到過 Iterator
trait 的定義如示例 19-12 所示:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
示例 19-12: Iterator
trait 的定義中帶有關(guān)聯(lián)類型 Item
Item
是一個(gè)占位類型,同時(shí) next
方法定義表明它返回 Option<Self::Item>
類型的值。這個(gè) trait 的實(shí)現(xiàn)者會(huì)指定 Item
的具體類型,然而不管實(shí)現(xiàn)者指定何種類型, next
方法都會(huì)返回一個(gè)包含了此具體類型值的 Option
。
關(guān)聯(lián)類型看起來像一個(gè)類似泛型的概念,因?yàn)樗试S定義一個(gè)函數(shù)而不指定其可以處理的類型。那么為什么要使用關(guān)聯(lián)類型呢?
讓我們通過一個(gè)在第十三章中出現(xiàn)的 Counter
結(jié)構(gòu)體上實(shí)現(xiàn) Iterator
trait 的例子來檢視其中的區(qū)別。在示例 13-21 中,指定了 Item
的類型為 u32
:
文件名: src/lib.rs
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
// --snip--
這類似于泛型。那么為什么 Iterator
trait 不像示例 19-13 那樣定義呢?
pub trait Iterator<T> {
fn next(&mut self) -> Option<T>;
}
示例 19-13: 一個(gè)使用泛型的 Iterator
trait 假想定義
區(qū)別在于當(dāng)如示例 19-13 那樣使用泛型時(shí),則不得不在每一個(gè)實(shí)現(xiàn)中標(biāo)注類型。這是因?yàn)槲覀円部梢詫?shí)現(xiàn)為 Iterator<String> for Counter
,或任何其他類型,這樣就可以有多個(gè) Counter
的 Iterator
的實(shí)現(xiàn)。換句話說,當(dāng) trait 有泛型參數(shù)時(shí),可以多次實(shí)現(xiàn)這個(gè) trait,每次需改變泛型參數(shù)的具體類型。接著當(dāng)使用 Counter
的 next
方法時(shí),必須提供類型注解來表明希望使用 Iterator
的哪一個(gè)實(shí)現(xiàn)。
通過關(guān)聯(lián)類型,則無需標(biāo)注類型,因?yàn)椴荒芏啻螌?shí)現(xiàn)這個(gè) trait。對(duì)于示例 19-12 使用關(guān)聯(lián)類型的定義,我們只能選擇一次 Item
會(huì)是什么類型,因?yàn)橹荒苡幸粋€(gè) impl Iterator for Counter
。當(dāng)調(diào)用 Counter
的 next
時(shí)不必每次指定我們需要 u32
值的迭代器。
當(dāng)使用泛型類型參數(shù)時(shí),可以為泛型指定一個(gè)默認(rèn)的具體類型。如果默認(rèn)類型就足夠的話,這消除了為具體類型實(shí)現(xiàn) trait 的需要。為泛型類型指定默認(rèn)類型的語法是在聲明泛型類型時(shí)使用 <PlaceholderType=ConcreteType>
。
這種情況的一個(gè)非常好的例子是用于運(yùn)算符重載。運(yùn)算符重載(Operator overloading)是指在特定情況下自定義運(yùn)算符(比如 +
)行為的操作。
Rust 并不允許創(chuàng)建自定義運(yùn)算符或重載任意運(yùn)算符,不過 std::ops
中所列出的運(yùn)算符和相應(yīng)的 trait 可以通過實(shí)現(xiàn)運(yùn)算符相關(guān) trait 來重載。例如,示例 19-14 中展示了如何在 Point
結(jié)構(gòu)體上實(shí)現(xiàn) Add
trait 來重載 +
運(yùn)算符,這樣就可以將兩個(gè) Point
實(shí)例相加了:
文件名: src/main.rs
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
assert_eq!(
Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 }
);
}
示例 19-14: 實(shí)現(xiàn) Add
trait 重載 Point
實(shí)例的 +
運(yùn)算符
add
方法將兩個(gè) Point
實(shí)例的 x
值和 y
值分別相加來創(chuàng)建一個(gè)新的 Point
。Add
trait 有一個(gè)叫做 Output
的關(guān)聯(lián)類型,它用來決定 add
方法的返回值類型。
這里默認(rèn)泛型類型位于 Add
trait 中。這里是其定義:
trait Add<Rhs=Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
這些代碼看來應(yīng)該很熟悉,這是一個(gè)帶有一個(gè)方法和一個(gè)關(guān)聯(lián)類型的 trait。比較陌生的部分是尖括號(hào)中的 Rhs=Self
:這個(gè)語法叫做 默認(rèn)類型參數(shù)(default type parameters)。Rhs
是一個(gè)泛型類型參數(shù)(“right hand side” 的縮寫),它用于定義 add
方法中的 rhs
參數(shù)。如果實(shí)現(xiàn) Add
trait 時(shí)不指定 Rhs
的具體類型,Rhs
的類型將是默認(rèn)的 Self
類型,也就是在其上實(shí)現(xiàn) Add
的類型。
當(dāng)為 Point
實(shí)現(xiàn) Add
時(shí),使用了默認(rèn)的 Rhs
,因?yàn)槲覀兿M麑蓚€(gè) Point
實(shí)例相加。讓我們看看一個(gè)實(shí)現(xiàn) Add
trait 時(shí)希望自定義 Rhs
類型而不是使用默認(rèn)類型的例子。
這里有兩個(gè)存放不同單元值的結(jié)構(gòu)體,Millimeters
和 Meters
。(這種將現(xiàn)有類型簡單封裝進(jìn)另一個(gè)結(jié)構(gòu)體的方式被稱為 newtype 模式(newtype pattern,之后的 “為了類型安全和抽象而使用 newtype 模式” 部分會(huì)詳細(xì)介紹。)我們希望能夠?qū)⒑撩字蹬c米值相加,并讓 Add
的實(shí)現(xiàn)正確處理轉(zhuǎn)換??梢詾?nbsp;Millimeters
實(shí)現(xiàn) Add
并以 Meters
作為 Rhs
,如示例 19-15 所示。
文件名: src/lib.rs
use std::ops::Add;
struct Millimeters(u32);
struct Meters(u32);
impl Add<Meters> for Millimeters {
type Output = Millimeters;
fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}
示例 19-15: 在 Millimeters
上實(shí)現(xiàn) Add
,以便能夠?qū)?nbsp;Millimeters
與 Meters
相加
為了使 Millimeters
和 Meters
能夠相加,我們指定 impl Add<Meters>
來設(shè)定 Rhs
類型參數(shù)的值而不是使用默認(rèn)的 Self
。
默認(rèn)參數(shù)類型主要用于如下兩個(gè)方面:
標(biāo)準(zhǔn)庫的 Add
trait 就是一個(gè)第二個(gè)目的例子:大部分時(shí)候你會(huì)將兩個(gè)相似的類型相加,不過它提供了自定義額外行為的能力。在 Add
trait 定義中使用默認(rèn)類型參數(shù)意味著大部分時(shí)候無需指定額外的參數(shù)。換句話說,一小部分實(shí)現(xiàn)的樣板代碼是不必要的,這樣使用 trait 就更容易了。
第一個(gè)目的是相似的,但過程是反過來的:如果需要為現(xiàn)有 trait 增加類型參數(shù),為其提供一個(gè)默認(rèn)類型將允許我們?cè)诓黄茐默F(xiàn)有實(shí)現(xiàn)代碼的基礎(chǔ)上擴(kuò)展 trait 的功能。
Rust 既不能避免一個(gè) trait 與另一個(gè) trait 擁有相同名稱的方法,也不能阻止為同一類型同時(shí)實(shí)現(xiàn)這兩個(gè) trait。甚至直接在類型上實(shí)現(xiàn)開始已經(jīng)有的同名方法也是可能的!
不過,當(dāng)調(diào)用這些同名方法時(shí),需要告訴 Rust 我們希望使用哪一個(gè)。考慮一下示例 19-16 中的代碼,這里定義了 trait Pilot
和 Wizard
都擁有方法 fly
。接著在一個(gè)本身已經(jīng)實(shí)現(xiàn)了名為 fly
方法的類型 Human
上實(shí)現(xiàn)這兩個(gè) trait。每一個(gè) fly
方法都進(jìn)行了不同的操作:
文件名: src/main.rs
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}
示例 19-16: 兩個(gè) trait 定義為擁有 fly
方法,并在直接定義有 fly
方法的 Human
類型上實(shí)現(xiàn)這兩個(gè) trait
當(dāng)調(diào)用 Human
實(shí)例的 fly
時(shí),編譯器默認(rèn)調(diào)用直接實(shí)現(xiàn)在類型上的方法,如示例 19-17 所示。
文件名: src/main.rs
fn main() {
let person = Human;
person.fly();
}
示例 19-17: 調(diào)用 Human
實(shí)例的 fly
運(yùn)行這段代碼會(huì)打印出 *waving arms furiously*
,這表明 Rust 調(diào)用了直接實(shí)現(xiàn)在 Human
上的 fly
方法。
為了能夠調(diào)用 Pilot
trait 或 Wizard
trait 的 fly
方法,我們需要使用更明顯的語法以便能指定我們指的是哪個(gè) fly
方法。這個(gè)語法展示在示例 19-18 中:
文件名: src/main.rs
fn main() {
let person = Human;
Pilot::fly(&person);
Wizard::fly(&person);
person.fly();
}
示例 19-18: 指定我們希望調(diào)用哪一個(gè) trait 的 fly
方法
在方法名前指定 trait 名向 Rust 澄清了我們希望調(diào)用哪個(gè) fly
實(shí)現(xiàn)。也可以選擇寫成 Human::fly(&person)
,這等同于示例 19-18 中的 person.fly()
,不過如果無需消歧義的話這么寫就有點(diǎn)長了。
運(yùn)行這段代碼會(huì)打印出:
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
Finished dev [unoptimized + debuginfo] target(s) in 0.46s
Running `target/debug/traits-example`
This is your captain speaking.
Up!
*waving arms furiously*
因?yàn)?nbsp;fly
方法獲取一個(gè) self
參數(shù),如果有兩個(gè) 類型 都實(shí)現(xiàn)了同一 trait,Rust 可以根據(jù) self
的類型計(jì)算出應(yīng)該使用哪一個(gè) trait 實(shí)現(xiàn)。
然而,關(guān)聯(lián)函數(shù)是 trait 的一部分,但沒有 self
參數(shù)。當(dāng)同一作用域的兩個(gè)類型實(shí)現(xiàn)了同一 trait,Rust 就不能計(jì)算出我們期望的是哪一個(gè)類型,除非使用 完全限定語法(fully qualified syntax)。例如,拿示例 19-19 中的 Animal
trait 來說,它有關(guān)聯(lián)函數(shù) baby_name
,結(jié)構(gòu)體 Dog
實(shí)現(xiàn)了 Animal
,同時(shí)有關(guān)聯(lián)函數(shù) baby_name
直接定義于 Dog
之上:
文件名: src/main.rs
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
fn main() {
println!("A baby dog is called a {}", Dog::baby_name());
}
示例 19-19: 一個(gè)帶有關(guān)聯(lián)函數(shù)的 trait 和一個(gè)帶有同名關(guān)聯(lián)函數(shù)并實(shí)現(xiàn)了此 trait 的類型
這段代碼用于一個(gè)動(dòng)物收容所,他們將所有的小狗起名為 Spot,這實(shí)現(xiàn)為定義于 Dog
之上的關(guān)聯(lián)函數(shù) baby_name
。Dog
類型還實(shí)現(xiàn)了 Animal
trait,它描述了所有動(dòng)物的共有的特征。小狗被稱為 puppy,這表現(xiàn)為 Dog
的 Animal
trait 實(shí)現(xiàn)中與 Animal
trait 相關(guān)聯(lián)的函數(shù) baby_name
。
在 main
調(diào)用了 Dog::baby_name
函數(shù),它直接調(diào)用了定義于 Dog
之上的關(guān)聯(lián)函數(shù)。這段代碼會(huì)打印出:
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
Finished dev [unoptimized + debuginfo] target(s) in 0.54s
Running `target/debug/traits-example`
A baby dog is called a Spot
這并不是我們需要的。我們希望調(diào)用的是 Dog
上 Animal
trait 實(shí)現(xiàn)那部分的 baby_name
函數(shù),這樣能夠打印出 A baby dog is called a puppy
。示例 19-18 中用到的技術(shù)在這并不管用;如果將 main
改為示例 19-20 中的代碼,則會(huì)得到一個(gè)編譯錯(cuò)誤:
文件名: src/main.rs
fn main() {
println!("A baby dog is called a {}", Animal::baby_name());
}
示例 19-20: 嘗試調(diào)用 Animal
trait 的 baby_name
函數(shù),不過 Rust 并不知道該使用哪一個(gè)實(shí)現(xiàn)
因?yàn)?nbsp;Animal::baby_name
是關(guān)聯(lián)函數(shù)而不是方法,因此它沒有 self
參數(shù),Rust 無法計(jì)算出所需的是哪一個(gè) Animal::baby_name
實(shí)現(xiàn)。我們會(huì)得到這個(gè)編譯錯(cuò)誤:
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
error[E0283]: type annotations needed
--> src/main.rs:20:43
|
20 | println!("A baby dog is called a {}", Animal::baby_name());
| ^^^^^^^^^^^^^^^^^ cannot infer type
|
= note: cannot satisfy `_: Animal`
For more information about this error, try `rustc --explain E0283`.
error: could not compile `traits-example` due to previous error
為了消歧義并告訴 Rust 我們希望使用的是 Dog
的 Animal
實(shí)現(xiàn),需要使用 完全限定語法,這是調(diào)用函數(shù)時(shí)最為明確的方式。示例 19-21 展示了如何使用完全限定語法:
文件名: src/main.rs
fn main() {
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
示例 19-21: 使用完全限定語法來指定我們希望調(diào)用的是 Dog
上 Animal
trait 實(shí)現(xiàn)中的 baby_name
函數(shù)
我們?cè)诩饫ㄌ?hào)中向 Rust 提供了類型注解,并通過在此函數(shù)調(diào)用中將 Dog
類型當(dāng)作 Animal
對(duì)待,來指定希望調(diào)用的是 Dog
上 Animal
trait 實(shí)現(xiàn)中的 baby_name
函數(shù)?,F(xiàn)在這段代碼會(huì)打印出我們期望的數(shù)據(jù):
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
Finished dev [unoptimized + debuginfo] target(s) in 0.48s
Running `target/debug/traits-example`
A baby dog is called a puppy
通常,完全限定語法定義為:
<Type as Trait>::function(receiver_if_method, next_arg, ...);
對(duì)于關(guān)聯(lián)函數(shù),其沒有一個(gè) receiver
,故只會(huì)有其他參數(shù)的列表??梢赃x擇在任何函數(shù)或方法調(diào)用處使用完全限定語法。然而,允許省略任何 Rust 能夠從程序中的其他信息中計(jì)算出的部分。只有當(dāng)存在多個(gè)同名實(shí)現(xiàn)而 Rust 需要幫助以便知道我們希望調(diào)用哪個(gè)實(shí)現(xiàn)時(shí),才需要使用這個(gè)較為冗長的語法。
有時(shí)我們可能會(huì)需要某個(gè) trait 使用另一個(gè) trait 的功能。在這種情況下,需要能夠依賴相關(guān)的 trait 也被實(shí)現(xiàn)。這個(gè)所需的 trait 是我們實(shí)現(xiàn)的 trait 的 父(超) trait(supertrait)。
例如我們希望創(chuàng)建一個(gè)帶有 outline_print
方法的 trait OutlinePrint
,它會(huì)打印出帶有星號(hào)框的值。也就是說,如果 Point
實(shí)現(xiàn)了 Display
并返回 (x, y)
,調(diào)用以 1
作為 x
和 3
作為 y
的 Point
實(shí)例的 outline_print
會(huì)顯示如下:
**********
* *
* (1, 3) *
* *
**********
在 outline_print
的實(shí)現(xiàn)中,因?yàn)橄M軌蚴褂?nbsp;Display
trait 的功能,則需要說明 OutlinePrint
只能用于同時(shí)也實(shí)現(xiàn)了 Display
并提供了 OutlinePrint
需要的功能的類型??梢酝ㄟ^在 trait 定義中指定 OutlinePrint: Display
來做到這一點(diǎn)。這類似于為 trait 增加 trait bound。示例 19-22 展示了一個(gè) OutlinePrint
trait 的實(shí)現(xiàn):
文件名: src/main.rs
use std::fmt;
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
示例 19-22: 實(shí)現(xiàn) OutlinePrint
trait,它要求來自 Display
的功能
因?yàn)橹付?nbsp;OutlinePrint
需要 Display
trait,則可以在 outline_print
中使用 to_string
, 其會(huì)為任何實(shí)現(xiàn) Display
的類型自動(dòng)實(shí)現(xiàn)。如果不在 trait 名后增加 : Display
并嘗試在 outline_print
中使用 to_string
,則會(huì)得到一個(gè)錯(cuò)誤說在當(dāng)前作用域中沒有找到用于 &Self
類型的方法 to_string
。
讓我們看看如果嘗試在一個(gè)沒有實(shí)現(xiàn) Display
的類型上實(shí)現(xiàn) OutlinePrint
會(huì)發(fā)生什么,比如 Point
結(jié)構(gòu)體:
文件名: src/main.rs
struct Point {
x: i32,
y: i32,
}
impl OutlinePrint for Point {}
這樣會(huì)得到一個(gè)錯(cuò)誤說 Display
是必須的而未被實(shí)現(xiàn):
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
error[E0277]: `Point` doesn't implement `std::fmt::Display`
--> src/main.rs:20:6
|
20 | impl OutlinePrint for Point {}
| ^^^^^^^^^^^^ `Point` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `Point`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
note: required by a bound in `OutlinePrint`
--> src/main.rs:3:21
|
3 | trait OutlinePrint: fmt::Display {
| ^^^^^^^^^^^^ required by this bound in `OutlinePrint`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `traits-example` due to previous error
一旦在 Point
上實(shí)現(xiàn) Display
并滿足 OutlinePrint
要求的限制,比如這樣:
文件名: src/main.rs
use std::fmt;
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
那么在 Point
上實(shí)現(xiàn) OutlinePrint
trait 將能成功編譯,并可以在 Point
實(shí)例上調(diào)用 outline_print
來顯示位于星號(hào)框中的點(diǎn)的值。
在第十章的 “為類型實(shí)現(xiàn) trait” 部分,我們提到了孤兒規(guī)則(orphan rule),它說明只要 trait 或類型對(duì)于當(dāng)前 crate 是本地的話就可以在此類型上實(shí)現(xiàn)該 trait。一個(gè)繞開這個(gè)限制的方法是使用 newtype 模式(newtype pattern),它涉及到在一個(gè)元組結(jié)構(gòu)體(第五章 “用沒有命名字段的元組結(jié)構(gòu)體來創(chuàng)建不同的類型” 部分介紹了元組結(jié)構(gòu)體)中創(chuàng)建一個(gè)新類型。這個(gè)元組結(jié)構(gòu)體帶有一個(gè)字段作為希望實(shí)現(xiàn) trait 的類型的簡單封裝。接著這個(gè)封裝類型對(duì)于 crate 是本地的,這樣就可以在這個(gè)封裝上實(shí)現(xiàn) trait。Newtype 是一個(gè)源自 (U.C.0079,逃) Haskell 編程語言的概念。使用這個(gè)模式?jīng)]有運(yùn)行時(shí)性能懲罰,這個(gè)封裝類型在編譯時(shí)就被省略了。
例如,如果想要在 Vec<T>
上實(shí)現(xiàn) Display
,而孤兒規(guī)則阻止我們直接這么做,因?yàn)?nbsp;Display
trait 和 Vec<T>
都定義于我們的 crate 之外。可以創(chuàng)建一個(gè)包含 Vec<T>
實(shí)例的 Wrapper
結(jié)構(gòu)體,接著可以如列表 19-23 那樣在 Wrapper
上實(shí)現(xiàn) Display
并使用 Vec<T>
的值:
文件名: src/main.rs
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}
示例 19-23: 創(chuàng)建 Wrapper
類型封裝 Vec<String>
以便能夠?qū)崿F(xiàn) Display
Display
的實(shí)現(xiàn)使用 self.0
來訪問其內(nèi)部的 Vec<T>
,因?yàn)?nbsp;Wrapper
是元組結(jié)構(gòu)體而 Vec<T>
是結(jié)構(gòu)體總位于索引 0 的項(xiàng)。接著就可以使用 Wrapper
中 Display
的功能了。
此方法的缺點(diǎn)是,因?yàn)?nbsp;Wrapper
是一個(gè)新類型,它沒有定義于其值之上的方法;必須直接在 Wrapper
上實(shí)現(xiàn) Vec<T>
的所有方法,這樣就可以代理到self.0
上 —— 這就允許我們完全像 Vec<T>
那樣對(duì)待 Wrapper
。如果希望新類型擁有其內(nèi)部類型的每一個(gè)方法,為封裝類型實(shí)現(xiàn) Deref
trait(第十五章 “通過 Deref trait 將智能指針當(dāng)作常規(guī)引用處理” 部分討論過)并返回其內(nèi)部類型是一種解決方案。如果不希望封裝類型擁有所有內(nèi)部類型的方法 —— 比如為了限制封裝類型的行為 —— 則必須只自行實(shí)現(xiàn)所需的方法。
上面便是 newtype 模式如何與 trait 結(jié)合使用的;還有一個(gè)不涉及 trait 的實(shí)用模式?,F(xiàn)在讓我們將話題的焦點(diǎn)轉(zhuǎn)移到一些與 Rust 類型系統(tǒng)交互的高級(jí)方法上來吧。
更多建議: