99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Rust Trait:定義共同行為

2023-03-22 15:10 更新
ch10-02-traits.md
commit 3c2ca8528c3b92b7d30e73f2e8a1b84b2f68b0c8

trait 告訴 Rust 編譯器某個(gè)特定類型擁有可能與其他類型共享的功能??梢酝ㄟ^(guò) trait 以一種抽象的方式定義共享的行為??梢允褂?nbsp;trait bounds 指定泛型是任何擁有特定行為的類型。

注意:trait 類似于其他語(yǔ)言中的常被稱為 接口interfaces)的功能,雖然有一些不同。

定義 trait

一個(gè)類型的行為由其可供調(diào)用的方法構(gòu)成。如果可以對(duì)不同類型調(diào)用相同的方法的話,這些類型就可以共享相同的行為了。trait 定義是一種將方法簽名組合起來(lái)的方法,目的是定義一個(gè)實(shí)現(xiàn)某些目的所必需的行為的集合。

例如,這里有多個(gè)存放了不同類型和屬性文本的結(jié)構(gòu)體:結(jié)構(gòu)體 NewsArticle 用于存放發(fā)生于世界各地的新聞故事,而結(jié)構(gòu)體 Tweet 最多只能存放 280 個(gè)字符的內(nèi)容,以及像是否轉(zhuǎn)推或是否是對(duì)推友的回復(fù)這樣的元數(shù)據(jù)。

我們想要?jiǎng)?chuàng)建一個(gè)名為 aggregator 的多媒體聚合庫(kù)用來(lái)顯示可能儲(chǔ)存在 NewsArticle 或 Tweet 實(shí)例中的數(shù)據(jù)的總結(jié)。每一個(gè)結(jié)構(gòu)體都需要的行為是他們是能夠被總結(jié)的,這樣的話就可以調(diào)用實(shí)例的 summarize 方法來(lái)請(qǐng)求總結(jié)。示例 10-12 中展示了一個(gè)表現(xiàn)這個(gè)概念的公有 Summary trait 的定義:

文件名: src/lib.rs

pub trait Summary {
    fn summarize(&self) -> String;
}

示例 10-12:Summary trait 定義,它包含由 summarize 方法提供的行為

這里使用 trait 關(guān)鍵字來(lái)聲明一個(gè) trait,后面是 trait 的名字,在這個(gè)例子中是 Summary。我們也聲明 trait 為 pub 以便依賴這個(gè) crate 的 crate 也可以使用這個(gè) trait,正如我們見(jiàn)過(guò)的一些示例一樣。在大括號(hào)中聲明描述實(shí)現(xiàn)這個(gè) trait 的類型所需要的行為的方法簽名,在這個(gè)例子中是 fn summarize(&self) -> String。

在方法簽名后跟分號(hào),而不是在大括號(hào)中提供其實(shí)現(xiàn)。接著每一個(gè)實(shí)現(xiàn)這個(gè) trait 的類型都需要提供其自定義行為的方法體,編譯器也會(huì)確保任何實(shí)現(xiàn) Summary trait 的類型都擁有與這個(gè)簽名的定義完全一致的 summarize 方法。

trait 體中可以有多個(gè)方法:一行一個(gè)方法簽名且都以分號(hào)結(jié)尾。

為類型實(shí)現(xiàn) trait

現(xiàn)在我們定義了 Summary trait 的簽名,接著就可以在多媒體聚合庫(kù)中實(shí)現(xiàn)這個(gè)類型了。示例 10-13 中展示了 NewsArticle 結(jié)構(gòu)體上 Summary trait 的一個(gè)實(shí)現(xiàn),它使用標(biāo)題、作者和創(chuàng)建的位置作為 summarize 的返回值。對(duì)于 Tweet 結(jié)構(gòu)體,我們選擇將 summarize 定義為用戶名后跟推文的全部文本作為返回值,并假設(shè)推文內(nèi)容已經(jīng)被限制為 280 字符以內(nèi)。

文件名: src/lib.rs

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

示例 10-13:在 NewsArticle 和 Tweet 類型上實(shí)現(xiàn) Summary trait

在類型上實(shí)現(xiàn) trait 類似于實(shí)現(xiàn)與 trait 無(wú)關(guān)的方法。區(qū)別在于 impl 關(guān)鍵字之后,我們提供需要實(shí)現(xiàn) trait 的名稱,接著是 for 和需要實(shí)現(xiàn) trait 的類型的名稱。在 impl 塊中,使用 trait 定義中的方法簽名,不過(guò)不再后跟分號(hào),而是需要在大括號(hào)中編寫函數(shù)體來(lái)為特定類型實(shí)現(xiàn) trait 方法所擁有的行為。

現(xiàn)在庫(kù)在 NewsArticle 和 Tweet 上實(shí)現(xiàn)了Summary trait,crate 的用戶可以像調(diào)用常規(guī)方法一樣調(diào)用 NewsArticle 和 Tweet 實(shí)例的 trait 方法了。唯一的區(qū)別是 trait 必須和類型一起引入作用域以便使用額外的 trait 方法。這是一個(gè)二進(jìn)制 crate 如何利用 aggregator 庫(kù) crate 的例子:

use aggregator::{Summary, Tweet};

fn main() {
    let tweet = Tweet {
        username: String::from("horse_ebooks"),
        content: String::from(
            "of course, as you probably already know, people",
        ),
        reply: false,
        retweet: false,
    };

    println!("1 new tweet: {}", tweet.summarize());
}

這會(huì)打印出 1 new tweet: horse_ebooks: of course, as you probably already know, people

其他依賴 aggregator crate 的 crate 也可以將 Summary 引入作用域以便為其自己的類型實(shí)現(xiàn)該 trait。實(shí)現(xiàn) trait 時(shí)需要注意的一個(gè)限制是,只有當(dāng)至少一個(gè) trait 或者要實(shí)現(xiàn) trait 的類型位于 crate 的本地作用域時(shí),才能為該類型實(shí)現(xiàn) trait。例如,可以為 aggregator crate 的自定義類型 Tweet 實(shí)現(xiàn)如標(biāo)準(zhǔn)庫(kù)中的 Display trait,這是因?yàn)?nbsp;Tweet 類型位于 aggregator crate 本地的作用域中。類似地,也可以在 aggregator crate 中為 Vec<T> 實(shí)現(xiàn) Summary,這是因?yàn)?nbsp;Summary trait 位于 aggregator crate 本地作用域中。

但是不能為外部類型實(shí)現(xiàn)外部 trait。例如,不能在 aggregator crate 中為 Vec<T> 實(shí)現(xiàn) Display trait。這是因?yàn)?nbsp;Display 和 Vec<T> 都定義于標(biāo)準(zhǔn)庫(kù)中,它們并不位于 aggregator crate 本地作用域中。這個(gè)限制是被稱為 相干性coherence) 的程序?qū)傩缘囊徊糠?,或者更具體的說(shuō)是 孤兒規(guī)則orphan rule),其得名于不存在父類型。這條規(guī)則確保了其他人編寫的代碼不會(huì)破壞你代碼,反之亦然。沒(méi)有這條規(guī)則的話,兩個(gè) crate 可以分別對(duì)相同類型實(shí)現(xiàn)相同的 trait,而 Rust 將無(wú)從得知應(yīng)該使用哪一個(gè)實(shí)現(xiàn)。

默認(rèn)實(shí)現(xiàn)

有時(shí)為 trait 中的某些或全部方法提供默認(rèn)的行為,而不是在每個(gè)類型的每個(gè)實(shí)現(xiàn)中都定義自己的行為是很有用的。這樣當(dāng)為某個(gè)特定類型實(shí)現(xiàn) trait 時(shí),可以選擇保留或重載每個(gè)方法的默認(rèn)行為。

示例 10-14 中展示了如何為 Summary trait 的 summarize 方法指定一個(gè)默認(rèn)的字符串值,而不是像示例 10-12 中那樣只是定義方法簽名:

文件名: src/lib.rs

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}

示例 10-14:Summary trait 的定義,帶有一個(gè) summarize 方法的默認(rèn)實(shí)現(xiàn)

如果想要對(duì) NewsArticle 實(shí)例使用這個(gè)默認(rèn)實(shí)現(xiàn),而不是定義一個(gè)自己的實(shí)現(xiàn),則可以通過(guò) impl Summary for NewsArticle {} 指定一個(gè)空的 impl 塊。

雖然我們不再直接為 NewsArticle 定義 summarize 方法了,但是我們提供了一個(gè)默認(rèn)實(shí)現(xiàn)并且指定 NewsArticle 實(shí)現(xiàn) Summary trait。因此,我們?nèi)匀豢梢詫?duì) NewsArticle 實(shí)例調(diào)用 summarize 方法,如下所示:

    let article = NewsArticle {
        headline: String::from("Penguins win the Stanley Cup Championship!"),
        location: String::from("Pittsburgh, PA, USA"),
        author: String::from("Iceburgh"),
        content: String::from(
            "The Pittsburgh Penguins once again are the best \
             hockey team in the NHL.",
        ),
    };

    println!("New article available! {}", article.summarize());

這段代碼會(huì)打印 New article available! (Read more...)。

為 summarize 創(chuàng)建默認(rèn)實(shí)現(xiàn)并不要求對(duì)示例 10-13 中 Tweet 上的 Summary 實(shí)現(xiàn)做任何改變。其原因是重載一個(gè)默認(rèn)實(shí)現(xiàn)的語(yǔ)法與實(shí)現(xiàn)沒(méi)有默認(rèn)實(shí)現(xiàn)的 trait 方法的語(yǔ)法一樣。

默認(rèn)實(shí)現(xiàn)允許調(diào)用相同 trait 中的其他方法,哪怕這些方法沒(méi)有默認(rèn)實(shí)現(xiàn)。如此,trait 可以提供很多有用的功能而只需要實(shí)現(xiàn)指定一小部分內(nèi)容。例如,我們可以定義 Summary trait,使其具有一個(gè)需要實(shí)現(xiàn)的 summarize_author 方法,然后定義一個(gè) summarize 方法,此方法的默認(rèn)實(shí)現(xiàn)調(diào)用 summarize_author 方法:

pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

為了使用這個(gè)版本的 Summary,只需在實(shí)現(xiàn) trait 時(shí)定義 summarize_author 即可:

impl Summary for Tweet {
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
}

一旦定義了 summarize_author,我們就可以對(duì) Tweet 結(jié)構(gòu)體的實(shí)例調(diào)用 summarize 了,而 summarize 的默認(rèn)實(shí)現(xiàn)會(huì)調(diào)用我們提供的 summarize_author 定義。因?yàn)閷?shí)現(xiàn)了 summarize_authorSummary trait 就提供了 summarize 方法的功能,且無(wú)需編寫更多的代碼。

    let tweet = Tweet {
        username: String::from("horse_ebooks"),
        content: String::from(
            "of course, as you probably already know, people",
        ),
        reply: false,
        retweet: false,
    };

    println!("1 new tweet: {}", tweet.summarize());

這會(huì)打印出 1 new tweet: (Read more from @horse_ebooks...)。

注意無(wú)法從相同方法的重載實(shí)現(xiàn)中調(diào)用默認(rèn)方法。

trait 作為參數(shù)

知道了如何定義 trait 和在類型上實(shí)現(xiàn)這些 trait 之后,我們可以探索一下如何使用 trait 來(lái)接受多種不同類型的參數(shù)。

例如在示例 10-13 中為 NewsArticle 和 Tweet 類型實(shí)現(xiàn)了 Summary trait。我們可以定義一個(gè)函數(shù) notify 來(lái)調(diào)用其參數(shù) item 上的 summarize 方法,該參數(shù)是實(shí)現(xiàn)了 Summary trait 的某種類型。為此可以使用 impl Trait 語(yǔ)法,像這樣:

pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

對(duì)于 item 參數(shù),我們指定了 impl 關(guān)鍵字和 trait 名稱,而不是具體的類型。該參數(shù)支持任何實(shí)現(xiàn)了指定 trait 的類型。在 notify 函數(shù)體中,可以調(diào)用任何來(lái)自 Summary trait 的方法,比如 summarize。我們可以傳遞任何 NewsArticle 或 Tweet 的實(shí)例來(lái)調(diào)用 notify。任何用其它如 String 或 i32 的類型調(diào)用該函數(shù)的代碼都不能編譯,因?yàn)樗鼈儧](méi)有實(shí)現(xiàn) Summary。

Trait Bound 語(yǔ)法

impl Trait 語(yǔ)法適用于直觀的例子,它實(shí)際上是一種較長(zhǎng)形式語(yǔ)法的語(yǔ)法糖。我們稱為 trait bound,它看起來(lái)像:

pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

這與之前的例子相同,不過(guò)稍微冗長(zhǎng)了一些。trait bound 與泛型參數(shù)聲明在一起,位于尖括號(hào)中的冒號(hào)后面。

impl Trait 很方便,適用于短小的例子。trait bound 則適用于更復(fù)雜的場(chǎng)景。例如,可以獲取兩個(gè)實(shí)現(xiàn)了 Summary 的參數(shù)。使用 impl Trait 的語(yǔ)法看起來(lái)像這樣:

pub fn notify(item1: &impl Summary, item2: &impl Summary) {

這適用于 item1 和 item2 允許是不同類型的情況(只要它們都實(shí)現(xiàn)了 Summary)。不過(guò)如果你希望強(qiáng)制它們都是相同類型呢?這只有在使用 trait bound 時(shí)才有可能:

pub fn notify<T: Summary>(item1: &T, item2: &T) {

泛型 T 被指定為 item1 和 item2 的參數(shù)限制,如此傳遞給參數(shù) item1 和 item2 值的具體類型必須一致。

通過(guò) + 指定多個(gè) trait bound

如果 notify 需要顯示 item 的格式化形式,同時(shí)也要使用 summarize 方法,那么 item 就需要同時(shí)實(shí)現(xiàn)兩個(gè)不同的 trait:Display 和 Summary。這可以通過(guò) + 語(yǔ)法實(shí)現(xiàn):

pub fn notify(item: &(impl Summary + Display)) {

+ 語(yǔ)法也適用于泛型的 trait bound:

pub fn notify<T: Summary + Display>(item: &T) {

通過(guò)指定這兩個(gè) trait bound,notify 的函數(shù)體可以調(diào)用 summarize 并使用 {} 來(lái)格式化 item。

通過(guò) where 簡(jiǎn)化 trait bound

然而,使用過(guò)多的 trait bound 也有缺點(diǎn)。每個(gè)泛型有其自己的 trait bound,所以有多個(gè)泛型參數(shù)的函數(shù)在名稱和參數(shù)列表之間會(huì)有很長(zhǎng)的 trait bound 信息,這使得函數(shù)簽名難以閱讀。為此,Rust 有另一個(gè)在函數(shù)簽名之后的 where 從句中指定 trait bound 的語(yǔ)法。所以除了這么寫:

fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {

還可以像這樣使用 where 從句:

fn some_function<T, U>(t: &T, u: &U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{

這個(gè)函數(shù)簽名就顯得不那么雜亂,函數(shù)名、參數(shù)列表和返回值類型都離得很近,看起來(lái)跟沒(méi)有那么多 trait bounds 的函數(shù)很像。

返回實(shí)現(xiàn)了 trait 的類型

也可以在返回值中使用 impl Trait 語(yǔ)法,來(lái)返回實(shí)現(xiàn)了某個(gè) trait 的類型:

fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from(
            "of course, as you probably already know, people",
        ),
        reply: false,
        retweet: false,
    }
}

通過(guò)使用 impl Summary 作為返回值類型,我們指定了 returns_summarizable 函數(shù)返回某個(gè)實(shí)現(xiàn)了 Summary trait 的類型,但是不確定其具體的類型。在這個(gè)例子中 returns_summarizable 返回了一個(gè) Tweet,不過(guò)調(diào)用方并不知情。

返回一個(gè)只是指定了需要實(shí)現(xiàn)的 trait 的類型的能力在閉包和迭代器場(chǎng)景十分的有用,第十三章會(huì)介紹它們。閉包和迭代器創(chuàng)建只有編譯器知道的類型,或者是非常非常長(zhǎng)的類型。impl Trait 允許你簡(jiǎn)單的指定函數(shù)返回一個(gè) Iterator 而無(wú)需寫出實(shí)際的冗長(zhǎng)的類型。

不過(guò)這只適用于返回單一類型的情況。例如,這段代碼的返回值類型指定為返回 impl Summary,但是返回了 NewsArticle 或 Tweet 就行不通:

fn returns_summarizable(switch: bool) -> impl Summary {
    if switch {
        NewsArticle {
            headline: String::from(
                "Penguins win the Stanley Cup Championship!",
            ),
            location: String::from("Pittsburgh, PA, USA"),
            author: String::from("Iceburgh"),
            content: String::from(
                "The Pittsburgh Penguins once again are the best \
                 hockey team in the NHL.",
            ),
        }
    } else {
        Tweet {
            username: String::from("horse_ebooks"),
            content: String::from(
                "of course, as you probably already know, people",
            ),
            reply: false,
            retweet: false,
        }
    }
}

這里嘗試返回 NewsArticle 或 Tweet。這不能編譯,因?yàn)?nbsp;impl Trait 工作方式的限制。第十七章的 “為使用不同類型的值而設(shè)計(jì)的 trait 對(duì)象” 部分會(huì)介紹如何編寫這樣一個(gè)函數(shù)。

使用 trait bounds 來(lái)修復(fù) largest 函數(shù)

現(xiàn)在你知道了如何使用泛型參數(shù) trait bound 來(lái)指定所需的行為。讓我們回到實(shí)例 10-5 修復(fù)使用泛型類型參數(shù)的 largest 函數(shù)定義!回顧一下,最后嘗試編譯代碼時(shí)出現(xiàn)的錯(cuò)誤是:

$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0369]: binary operation `>` cannot be applied to type `T`
 --> src/main.rs:5:17
  |
5 |         if item > largest {
  |            ---- ^ ------- T
  |            |
  |            T
  |
help: consider restricting type parameter `T`
  |
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {
  |             ++++++++++++++++++++++

For more information about this error, try `rustc --explain E0369`.
error: could not compile `chapter10` due to previous error

在 largest 函數(shù)體中我們想要使用大于運(yùn)算符(>)比較兩個(gè) T 類型的值。這個(gè)運(yùn)算符被定義為標(biāo)準(zhǔn)庫(kù)中 trait std::cmp::PartialOrd 的一個(gè)默認(rèn)方法。所以需要在 T 的 trait bound 中指定 PartialOrd,這樣 largest 函數(shù)可以用于任何可以比較大小的類型的 slice。因?yàn)?nbsp;PartialOrd 位于 prelude 中所以并不需要手動(dòng)將其引入作用域。將 largest 的簽名修改為如下:

fn largest<T: PartialOrd>(list: &[T]) -> T {

但是如果編譯代碼的話,會(huì)出現(xiàn)一些不同的錯(cuò)誤:

$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0508]: cannot move out of type `[T]`, a non-copy slice
 --> src/main.rs:2:23
  |
2 |     let mut largest = list[0];
  |                       ^^^^^^^
  |                       |
  |                       cannot move out of here
  |                       move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait
  |                       help: consider borrowing here: `&list[0]`

error[E0507]: cannot move out of a shared reference
 --> src/main.rs:4:18
  |
4 |     for &item in list {
  |         -----    ^^^^
  |         ||
  |         |data moved here
  |         |move occurs because `item` has type `T`, which does not implement the `Copy` trait
  |         help: consider removing the `&`: `item`

Some errors have detailed explanations: E0507, E0508.
For more information about an error, try `rustc --explain E0507`.
error: could not compile `chapter10` due to 2 previous errors

錯(cuò)誤的核心是 cannot move out of type [T], a non-copy slice,對(duì)于非泛型版本的 largest 函數(shù),我們只嘗試了尋找最大的 i32 和 char。正如第四章 “只在棧上的數(shù)據(jù):拷貝” 部分討論過(guò)的,像 i32 和 char 這樣的類型是已知大小的并可以儲(chǔ)存在棧上,所以他們實(shí)現(xiàn)了 Copy trait。當(dāng)我們將 largest 函數(shù)改成使用泛型后,現(xiàn)在 list 參數(shù)的類型就有可能是沒(méi)有實(shí)現(xiàn) Copy trait 的。這意味著我們可能不能將 list[0] 的值移動(dòng)到 largest 變量中,這導(dǎo)致了上面的錯(cuò)誤。

為了只對(duì)實(shí)現(xiàn)了 Copy 的類型調(diào)用這些代碼,可以在 T 的 trait bounds 中增加 Copy!示例 10-15 中展示了一個(gè)可以編譯的泛型版本的 largest 函數(shù)的完整代碼,只要傳遞給 largest 的 slice 值的類型實(shí)現(xiàn)了 PartialOrd  Copy 這兩個(gè) trait,例如 i32 和 char

文件名: src/main.rs

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

示例 10-15:一個(gè)可以用于任何實(shí)現(xiàn)了 PartialOrd 和 Copy trait 的泛型的 largest 函數(shù)

如果并不希望限制 largest 函數(shù)只能用于實(shí)現(xiàn)了 Copy trait 的類型,我們可以在 T 的 trait bounds 中指定 Clone 而不是 Copy。并克隆 slice 的每一個(gè)值使得 largest 函數(shù)擁有其所有權(quán)。使用 clone 函數(shù)意味著對(duì)于類似 String 這樣擁有堆上數(shù)據(jù)的類型,會(huì)潛在的分配更多堆上空間,而堆分配在涉及大量數(shù)據(jù)時(shí)可能會(huì)相當(dāng)緩慢。

另一種 largest 的實(shí)現(xiàn)方式是返回在 slice 中 T 值的引用。如果我們將函數(shù)返回值從 T 改為 &T 并改變函數(shù)體使其能夠返回一個(gè)引用,我們將不需要任何 Clone 或 Copy 的 trait bounds 而且也不會(huì)有任何的堆分配。嘗試自己實(shí)現(xiàn)這種替代解決方式吧!如果你無(wú)法擺脫與生命周期有關(guān)的錯(cuò)誤,請(qǐng)繼續(xù)閱讀:接下來(lái)的 “生命周期與引用有效性” 部分會(huì)詳細(xì)的說(shuō)明,不過(guò)生命周期對(duì)于解決這些挑戰(zhàn)來(lái)說(shuō)并不是必須的。

使用 trait bound 有條件地實(shí)現(xiàn)方法

通過(guò)使用帶有 trait bound 的泛型參數(shù)的 impl 塊,可以有條件地只為那些實(shí)現(xiàn)了特定 trait 的類型實(shí)現(xiàn)方法。例如,示例 10-16 中的類型 Pair<T> 總是實(shí)現(xiàn)了 new 方法并返回一個(gè) Pair<T> 的實(shí)例(回憶一下第五章的 "定義方法" 部分,Self 是一個(gè) impl 塊類型的類型別名(type alias),在這里是 Pair<T>)。不過(guò)在下一個(gè) impl 塊中,只有那些為 T 類型實(shí)現(xiàn)了 PartialOrd trait (來(lái)允許比較)  Display trait (來(lái)啟用打?。┑?nbsp;Pair<T> 才會(huì)實(shí)現(xiàn) cmp_display 方法:

use std::fmt::Display;

struct Pair<T> {
    x: T,
    y: T,
}

impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("The largest member is x = {}", self.x);
        } else {
            println!("The largest member is y = {}", self.y);
        }
    }
}

示例 10-16:根據(jù) trait bound 在泛型上有條件的實(shí)現(xiàn)方法

也可以對(duì)任何實(shí)現(xiàn)了特定 trait 的類型有條件地實(shí)現(xiàn) trait。對(duì)任何滿足特定 trait bound 的類型實(shí)現(xiàn) trait 被稱為 blanket implementations,他們被廣泛的用于 Rust 標(biāo)準(zhǔn)庫(kù)中。例如,標(biāo)準(zhǔn)庫(kù)為任何實(shí)現(xiàn)了 Display trait 的類型實(shí)現(xiàn)了 ToString trait。這個(gè) impl 塊看起來(lái)像這樣:

impl<T: Display> ToString for T {
    // --snip--
}

因?yàn)闃?biāo)準(zhǔn)庫(kù)有了這些 blanket implementation,我們可以對(duì)任何實(shí)現(xiàn)了 Display trait 的類型調(diào)用由 ToString 定義的 to_string 方法。例如,可以將整型轉(zhuǎn)換為對(duì)應(yīng)的 String 值,因?yàn)檎蛯?shí)現(xiàn)了 Display

let s = 3.to_string();

blanket implementation 會(huì)出現(xiàn)在 trait 文檔的 “Implementers” 部分。

trait 和 trait bound 讓我們使用泛型類型參數(shù)來(lái)減少重復(fù),并仍然能夠向編譯器明確指定泛型類型需要擁有哪些行為。因?yàn)槲覀兿蚓幾g器提供了 trait bound 信息,它就可以檢查代碼中所用到的具體類型是否提供了正確的行為。在動(dòng)態(tài)類型語(yǔ)言中,如果我們嘗試調(diào)用一個(gè)類型并沒(méi)有實(shí)現(xiàn)的方法,會(huì)在運(yùn)行時(shí)出現(xiàn)錯(cuò)誤。Rust 將這些錯(cuò)誤移動(dòng)到了編譯時(shí),甚至在代碼能夠運(yùn)行之前就強(qiáng)迫我們修復(fù)錯(cuò)誤。另外,我們也無(wú)需編寫運(yùn)行時(shí)檢查行為的代碼,因?yàn)樵诰幾g時(shí)就已經(jīng)檢查過(guò)了,這樣相比其他那些不愿放棄泛型靈活性的語(yǔ)言有更好的性能。

這里還有一種泛型,我們一直在使用它甚至都沒(méi)有察覺(jué)它的存在,這就是 生命周期lifetimes)。不同于其他泛型幫助我們確保類型擁有期望的行為,生命周期則有助于確保引用在我們需要他們的時(shí)候一直有效。讓我們學(xué)習(xí)生命周期是如何做到這些的。


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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)