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

Rust 方法語法

2023-03-22 15:09 更新
ch05-03-method-syntax.md
commit dd7e05275822d6cf790bcdae6983b3234141b5e7

方法(method)與函數(shù)類似:它們使用 fn 關(guān)鍵字和名稱聲明,可以擁有參數(shù)和返回值,同時(shí)包含在某處調(diào)用該方法時(shí)會(huì)執(zhí)行的代碼。不過方法與函數(shù)是不同的,因?yàn)樗鼈冊(cè)诮Y(jié)構(gòu)體的上下文中被定義(或者是枚舉或 trait 對(duì)象的上下文,將分別在第六章和第十七章講解),并且它們第一個(gè)參數(shù)總是 self,它代表調(diào)用該方法的結(jié)構(gòu)體實(shí)例。

定義方法

讓我們把前面實(shí)現(xiàn)的獲取一個(gè) Rectangle 實(shí)例作為參數(shù)的 area 函數(shù),改寫成一個(gè)定義于 Rectangle 結(jié)構(gòu)體上的 area 方法,如示例 5-13 所示:

文件名: src/main.rs

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

示例 5-13:在 Rectangle 結(jié)構(gòu)體上定義 area 方法

為了使函數(shù)定義于 Rectangle 的上下文中,我們開始了一個(gè) impl 塊(impl 是 implementation 的縮寫),這個(gè) impl 塊中的所有內(nèi)容都將與 Rectangle 類型相關(guān)聯(lián)。接著將 area 函數(shù)移動(dòng)到 impl 大括號(hào)中,并將簽名中的第一個(gè)(在這里也是唯一一個(gè))參數(shù)和函數(shù)體中其他地方的對(duì)應(yīng)參數(shù)改成 self。然后在 main 中將我們先前調(diào)用 area 方法并傳遞 rect1 作為參數(shù)的地方,改成使用 方法語法method syntax)在 Rectangle 實(shí)例上調(diào)用 area 方法。方法語法獲取一個(gè)實(shí)例并加上一個(gè)點(diǎn)號(hào),后跟方法名、圓括號(hào)以及任何參數(shù)。

在 area 的簽名中,使用 &self 來替代 rectangle: &Rectangle,&self 實(shí)際上是 self: &Self 的縮寫。在一個(gè) impl 塊中,Self 類型是 impl 塊的類型的別名。方法的第一個(gè)參數(shù)必須有一個(gè)名為 self 的Self 類型的參數(shù),所以 Rust 讓你在第一個(gè)參數(shù)位置上只用 self 這個(gè)名字來縮寫。注意,我們?nèi)匀恍枰?nbsp;self 前面使用 & 來表示這個(gè)方法借用了 Self 實(shí)例,就像我們?cè)?nbsp;rectangle: &Rectangle 中做的那樣。方法可以選擇獲得 self 的所有權(quán),或者像我們這里一樣不可變地借用 self,或者可變地借用 self,就跟其他參數(shù)一樣。

這里選擇 &self 的理由跟在函數(shù)版本中使用 &Rectangle 是相同的:我們并不想獲取所有權(quán),只希望能夠讀取結(jié)構(gòu)體中的數(shù)據(jù),而不是寫入。如果想要在方法中改變調(diào)用方法的實(shí)例,需要將第一個(gè)參數(shù)改為 &mut self。通過僅僅使用 self 作為第一個(gè)參數(shù)來使方法獲取實(shí)例的所有權(quán)是很少見的;這種技術(shù)通常用在當(dāng)方法將 self 轉(zhuǎn)換成別的實(shí)例的時(shí)候,這時(shí)我們想要防止調(diào)用者在轉(zhuǎn)換之后使用原始的實(shí)例。

使用方法替代函數(shù),除了可使用方法語法和不需要在每個(gè)函數(shù)簽名中重復(fù) self 的類型之外,其主要好處在于組織性。我們將某個(gè)類型實(shí)例能做的所有事情都一起放入 impl 塊中,而不是讓將來的用戶在我們的庫中到處尋找 Rectangle 的功能。

請(qǐng)注意,我們可以選擇將方法的名稱與結(jié)構(gòu)中的一個(gè)字段相同。例如,我們可以在 Rectangle 上定義一個(gè)方法,并命名為 width

文件名: src/main.rs

impl Rectangle {
    fn width(&self) -> bool {
        self.width > 0
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    if rect1.width() {
        println!("The rectangle has a nonzero width; it is {}", rect1.width);
    }
}

在這里,我們選擇讓 width 方法在實(shí)例的 width 字段的值大于 0 時(shí)返回 true,等于 0 時(shí)則返回 false:我們可以出于任何目的,在同名的方法中使用同名的字段。在 main 中,當(dāng)我們?cè)?nbsp;rect1.width 后面加上括號(hào)時(shí)。Rust 知道我們指的是方法 width。當(dāng)我們不使用圓括號(hào)時(shí),Rust 知道我們指的是字段 width。

通常,但并不總是如此,與字段同名的方法將被定義為只返回字段中的值,而不做其他事情。這樣的方法被稱為 getters,Rust 并不像其他一些語言那樣為結(jié)構(gòu)字段自動(dòng)實(shí)現(xiàn)它們。Getters 很有用,因?yàn)槟憧梢园炎侄巫兂伤接械?,但方法是公共的,這樣就可以把對(duì)字段的只讀訪問作為該類型公共 API 的一部分。我們將在第七章中討論什么是公有和私有,以及如何將一個(gè)字段或方法指定為公有或私有。

-> 運(yùn)算符到哪去了?

在 C/C++ 語言中,有兩個(gè)不同的運(yùn)算符來調(diào)用方法:. 直接在對(duì)象上調(diào)用方法,而 -> 在一個(gè)對(duì)象的指針上調(diào)用方法,這時(shí)需要先解引用(dereference)指針。換句話說,如果 object 是一個(gè)指針,那么 object->something() 就像 (*object).something() 一樣。

Rust 并沒有一個(gè)與 -> 等效的運(yùn)算符;相反,Rust 有一個(gè)叫 自動(dòng)引用和解引用automatic referencing and dereferencing)的功能。方法調(diào)用是 Rust 中少數(shù)幾個(gè)擁有這種行為的地方。

它是這樣工作的:當(dāng)使用 object.something() 調(diào)用方法時(shí),Rust 會(huì)自動(dòng)為 object 添加 &、&mut 或 * 以便使 object 與方法簽名匹配。也就是說,這些代碼是等價(jià)的:

p1.distance(&p2);
(&p1).distance(&p2);

第一行看起來簡潔的多。這種自動(dòng)引用的行為之所以有效,是因?yàn)榉椒ㄓ幸粋€(gè)明確的接收者———— self 的類型。在給出接收者和方法名的前提下,Rust 可以明確地計(jì)算出方法是僅僅讀?。?code>&self),做出修改(&mut self)或者是獲取所有權(quán)(self)。事實(shí)上,Rust 對(duì)方法接收者的隱式借用讓所有權(quán)在實(shí)踐中更友好。

帶有更多參數(shù)的方法

讓我們通過實(shí)現(xiàn) Rectangle 結(jié)構(gòu)體上的另一方法來練習(xí)使用方法。這回,我們讓一個(gè) Rectangle 的實(shí)例獲取另一個(gè) Rectangle 實(shí)例,如果 self (第一個(gè) Rectangle)能完全包含第二個(gè)長方形則返回 true;否則返回 false。一旦我們定義了 can_hold 方法,就可以編寫示例 5-14 中的代碼。

文件名: src/main.rs

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

示例 5-14:使用還未實(shí)現(xiàn)的 can_hold 方法

同時(shí)我們希望看到如下輸出,因?yàn)?nbsp;rect2 的兩個(gè)維度都小于 rect1,而 rect3 比 rect1 要寬:

Can rect1 hold rect2? true
Can rect1 hold rect3? false

因?yàn)槲覀兿攵x一個(gè)方法,所以它應(yīng)該位于 impl Rectangle 塊中。方法名是 can_hold,并且它會(huì)獲取另一個(gè) Rectangle 的不可變借用作為參數(shù)。通過觀察調(diào)用方法的代碼可以看出參數(shù)是什么類型的:rect1.can_hold(&rect2) 傳入了 &rect2,它是一個(gè) Rectangle 的實(shí)例 rect2 的不可變借用。這是可以理解的,因?yàn)槲覀冎恍枰x取 rect2(而不是寫入,這意味著我們需要一個(gè)不可變借用),而且希望 main 保持 rect2 的所有權(quán),這樣就可以在調(diào)用這個(gè)方法后繼續(xù)使用它。can_hold 的返回值是一個(gè)布爾值,其實(shí)現(xiàn)會(huì)分別檢查 self 的寬高是否都大于另一個(gè) Rectangle。讓我們?cè)谑纠?5-13 的 impl 塊中增加這個(gè)新的 can_hold 方法,如示例 5-15 所示:

文件名: src/main.rs

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

示例 5-15:在 Rectangle 上實(shí)現(xiàn) can_hold 方法,它獲取另一個(gè) Rectangle 實(shí)例作為參數(shù)

如果結(jié)合示例 5-14 的 main 函數(shù)來運(yùn)行,就會(huì)看到期望的輸出。在方法簽名中,可以在 self 后增加多個(gè)參數(shù),而且這些參數(shù)就像函數(shù)中的參數(shù)一樣工作。

關(guān)聯(lián)函數(shù)

所有在 impl 塊中定義的函數(shù)被稱為 關(guān)聯(lián)函數(shù)associated functions),因?yàn)樗鼈兣c impl 后面命名的類型相關(guān)。我們可以定義不以 self 為第一參數(shù)的關(guān)聯(lián)函數(shù)(因此不是方法),因?yàn)樗鼈儾⒉蛔饔糜谝粋€(gè)結(jié)構(gòu)體的實(shí)例。我們已經(jīng)使用了一個(gè)這樣的函數(shù):在 String 類型上定義的 String::from 函數(shù)。

不是方法的關(guān)聯(lián)函數(shù)經(jīng)常被用作返回一個(gè)結(jié)構(gòu)體新實(shí)例的構(gòu)造函數(shù)。這些函數(shù)的名稱通常為 new ,但 new 并不是一個(gè)關(guān)鍵字。例如我們可以提供一個(gè)叫做 square 關(guān)聯(lián)函數(shù),它接受一個(gè)維度參數(shù)并且同時(shí)作為寬和高,這樣可以更輕松的創(chuàng)建一個(gè)正方形 Rectangle 而不必指定兩次同樣的值:

文件名: src/main.rs

impl Rectangle {
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

關(guān)鍵字 Self 在函數(shù)的返回類型中代指在 impl 關(guān)鍵字后出現(xiàn)的類型,在這里是 Rectangle

使用結(jié)構(gòu)體名和 :: 語法來調(diào)用這個(gè)關(guān)聯(lián)函數(shù):比如 let sq = Rectangle::square(3);。這個(gè)函數(shù)位于結(jié)構(gòu)體的命名空間中::: 語法用于關(guān)聯(lián)函數(shù)和模塊創(chuàng)建的命名空間。第七章會(huì)講到模塊。

多個(gè) impl 塊

每個(gè)結(jié)構(gòu)體都允許擁有多個(gè) impl 塊。例如,示例 5-16 中的代碼等同于示例 5-15,但每個(gè)方法有其自己的 impl 塊。

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

示例 5-16:使用多個(gè) impl 塊重寫示例 5-15

這里沒有理由將這些方法分散在多個(gè) impl 塊中,不過這是有效的語法。第十章討論泛型和 trait 時(shí)會(huì)看到實(shí)用的多 impl 塊的用例。

總結(jié)

結(jié)構(gòu)體讓你可以創(chuàng)建出在你的領(lǐng)域中有意義的自定義類型。通過結(jié)構(gòu)體,我們可以將相關(guān)聯(lián)的數(shù)據(jù)片段聯(lián)系起來并命名它們,這樣可以使得代碼更加清晰。在 impl 塊中,你可以定義與你的類型相關(guān)聯(lián)的函數(shù),而方法是一種相關(guān)聯(lián)的函數(shù),讓你指定結(jié)構(gòu)體的實(shí)例所具有的行為。

但結(jié)構(gòu)體并不是創(chuàng)建自定義類型的唯一方法:讓我們轉(zhuǎn)向 Rust 的枚舉功能,為你的工具箱再添一個(gè)工具。


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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)