Rust 枚舉和模式匹配的實(shí)現(xiàn)
1、枚舉的定義
枚舉(enumerations),也被稱作 enums。枚舉允許你通過列舉可能的 成員(variants)來定義一個(gè)類型。首先,我們會(huì)定義并使用一個(gè)枚舉來展示它是如何連同數(shù)據(jù)一起編碼信息的。接下來,我們會(huì)探索一個(gè)特別有用的枚舉,叫做 Option,它代表一個(gè)值要么是某個(gè)值要么什么都不是。然后會(huì)講到在 match 表達(dá)式中用模式匹配,針對(duì)不同的枚舉值編寫相應(yīng)要執(zhí)行的代碼。最后會(huì)介紹 if let,另一個(gè)簡(jiǎn)潔方便處理代碼中枚舉的結(jié)構(gòu)。
下面看下下面這個(gè)示例:
#[derive(Debug)]
enum Sex {
Man,
Woman,
}
fn main() {
let var = Sex::Man;
println!("value is {:?}", var)
}從上面代碼示例中,我們把性別可以枚舉出來,引用枚舉類型的某一個(gè)值的時(shí)候,可以通過枚舉名后面加一堆冒號(hào)來引用枚舉中的某一個(gè)屬性值。
下面這個(gè)例子,我們可以在枚舉中,它的成員可以有多種類型:
enum op {
name(String),
time(i32),
People { name: String, age: i32 },
}有關(guān)聯(lián)值的枚舉的方式和定義多個(gè)不同類型的結(jié)構(gòu)體的方式很相像,除了枚舉不使用 struct 關(guān)鍵字以及其所有成員都被組合在一起。
結(jié)構(gòu)體和枚舉還有另一個(gè)相似點(diǎn):就像可以使用
impl來為結(jié)構(gòu)體定義方法那樣,也可以在枚舉上定義方法。
enum Op {
Name(String),
Time(i32),
People { name: String, age: i32 },
}
impl Op {
fn say(&self) {}
}讓我們看看標(biāo)準(zhǔn)庫(kù)中的另一個(gè)非常常見且實(shí)用的枚舉:Option。
1.1 Option 枚舉和其相對(duì)于空值的優(yōu)勢(shì)
這一部分會(huì)分析一個(gè) Option 的案例,Option 是標(biāo)準(zhǔn)庫(kù)定義的另一個(gè)枚舉。Option 類型應(yīng)用廣泛因?yàn)樗幋a了一個(gè)非常普遍的場(chǎng)景,即一個(gè)值要么有值要么沒值。
例如,如果請(qǐng)求一個(gè)非空列表的第一項(xiàng),會(huì)得到一個(gè)值,如果請(qǐng)求一個(gè)空的列表,就什么也不會(huì)得到。從類型系統(tǒng)的角度來表達(dá)這個(gè)概念就意味著編譯器需要檢查是否處理了所有應(yīng)該處理的情況,這樣就可以避免在其他編程語言中非常常見的 bug。
編程語言的設(shè)計(jì)經(jīng)常要考慮包含哪些功能,但考慮排除哪些功能也很重要。Rust 并沒有很多其他語言中有的空值功能。空值(Null )是一個(gè)值,它代表沒有值。在有空值的語言中,變量總是這兩種狀態(tài)之一:空值和非空值。
然而,空值嘗試表達(dá)的概念仍然是有意義的:空值是一個(gè)因?yàn)槟撤N原因目前無效或缺失的值。
問題不在于概念而在于具體的實(shí)現(xiàn)。為此,Rust 并沒有空值,不過它確實(shí)擁有一個(gè)可以編碼存在或不存在概念的枚舉。這個(gè)枚舉是 Option<T>,而且它定義于標(biāo)準(zhǔn)庫(kù)中,如下:
fn main() {
enum Option<T> {
None,
Some(T),
}
}
Option<T> 也仍是常規(guī)的枚舉,Some(T) 和 None 仍是 Option<T> 的成員。<T> 語法是一個(gè)我們還未講到的 Rust 功能。它是一個(gè)泛型類型參數(shù),所以你需要知道的就是 <T> 意味著 Option 枚舉的 Some 成員可以包含任意類型的數(shù)據(jù),同時(shí)每一個(gè)用于 T 位置的具體類型使得 Option<T> 整體作為不同的類型。這里是一些包含數(shù)字類型和字符串類型 Option 值的例子:
enum Option<T> {
None,
Some(T),
}
let some_number = Some(5000);
let some_char = Some('e');
let some_boolean = Some(true);讓我們?cè)倏匆幌氯缦率纠x如下2個(gè)值進(jìn)行相加會(huì)怎么樣?
fn main() {
enum Option<T> {
None,
Some(T),
}
let some_number: i8 = 5;
let absent_number: Option<i8> = Some(5);
let plus = some_number + absent_number;
}運(yùn)行結(jié)果如下所示:

在這里有2個(gè)嚴(yán)重的問題:
第一個(gè)問題是let absent_number: Option<i8> = Some(5); 在這里賦值的時(shí)候會(huì)報(bào)錯(cuò),這2個(gè)類型名看起來很像,但實(shí)際上是不同的類型,無法進(jìn)行賦值操作。
第二個(gè)是不同類型進(jìn)行相加的時(shí)候,當(dāng)在 Rust 中擁有一個(gè)像 i8 這樣類型的值時(shí),編譯器確保它總是有一個(gè)有效的值。我們可以自信使用而無需做空值檢查。只有當(dāng)使用 Option<i8>(或者任何用到的類型)的時(shí)候需要擔(dān)心可能沒有值,而編譯器會(huì)確保我們?cè)谑褂弥抵疤幚砹藶榭盏那闆r。
2、match 控制流結(jié)構(gòu)
Rust 有一個(gè)叫做 match 的極為強(qiáng)大的控制流運(yùn)算符,它允許我們將一個(gè)值與一系列的模式相比較,并根據(jù)相匹配的模式執(zhí)行相應(yīng)代碼。模式可由字面值、變量、通配符和許多其他內(nèi)容構(gòu)成;
我們看一下如下示例,能夠更清楚的明白match的作用:
fn main() {
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
let res = value_in_cents(Coin::Nickel);
print!("result {}", res) // result 5
}match 的作用,其他跟其他語言(例如,JavaScript)中的switch差不多,以上代碼中,方法接收了一個(gè)枚舉類型,match根據(jù)枚舉類型的不同成員來返回的不同的值,類似不同的分支,符合條件的分支,才會(huì)被最后返回,如果匹配到了某一個(gè)分支,想在執(zhí)行其他邏輯的時(shí)候,可以加一對(duì)花括號(hào),在里面寫對(duì)應(yīng)的邏輯即可。
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
print!("res: 執(zhí)行到這了");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}2.1 匹配 Option<T>
下面編寫一個(gè)函數(shù),它獲取一個(gè) Option<i32> ,如果其中含有一個(gè)值,將其加一。如果其中沒有值,函數(shù)應(yīng)該返回 None 值。
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
println!("{:?} {:?} {:?}", five, six, none) // Some(5) Some(6) None2.2 匹配是窮盡的
match 還有另一方面需要討論:這些分支必須覆蓋了所有的可能性。否則不能進(jìn)行編譯。
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),
}
}
根據(jù)上面錯(cuò)誤提示,我們知道Rust中match匹配必須是窮盡的,否則無法編譯通過。
2.3 通配模式和 _ 占位符
我們看一下如下示例:
fn main() {
let dice_roll = 9;
match dice_roll {
3 => 3,
7 => 7,
hello => 9,
};
}3和7會(huì)匹配對(duì)應(yīng)的值,定義一個(gè)變量例如:hello,則可以匹配其他任意情況下的值。
即使我們沒有列出 u8 所有可能的值,這段代碼依然能夠編譯,因?yàn)樽詈笠粋€(gè)模式將匹配所有未被特殊列出的值。這種通配模式滿足了 match 必須被窮盡的要求。請(qǐng)注意,我們必須將通配分支放在最后,因?yàn)槟J绞前错樞蚱ヅ涞摹H绻覀冊(cè)谕ㄅ浞种Ш筇砑悠渌种?,Rust 將會(huì)警告我們,因?yàn)榇撕蟮姆种в肋h(yuǎn)不會(huì)被匹配到。
Rust 還提供了一個(gè)模式,當(dāng)我們不想使用通配模式獲取的值時(shí),請(qǐng)使用 _ ,這是一個(gè)特殊的模式,可以匹配任意值而不綁定到該值。這告訴 Rust 我們不會(huì)使用這個(gè)值,所以 Rust 也不會(huì)警告我們存在未使用的變量。
fn main() {
let dice_roll = 9;
match dice_roll {
3 => 3,
7 => 7,
_ => 9,
};
}當(dāng)我們匹配到其他情況,這種情況下我們不想運(yùn)行任何代碼??梢苑祷匾粋€(gè)空元組,如下所示:
fn main() {
let dice_roll = 9;
match dice_roll {
3 => three(),
7 => seven(),
_ => (),
}
fn three() {}
fn seven() {}
}3、if let 簡(jiǎn)潔控制流
我們先看一個(gè)示例:
fn main() {
let config_max = Some(3u8);
match config_max {
Some(max) => println!("The maximum is configured to be {}", max),
_ => (),
}
}
如果值是 Some,我們希望打印出 Some 成員中的值,這個(gè)值被綁定到模式中的 max 變量里。對(duì)于 None 值我們不希望做任何操作。為了滿足 match 表達(dá)式(窮盡性)的要求,必須在處理完這唯一的成員后加上 _ => (),這樣也要增加很多煩人的樣板代碼。
為了簡(jiǎn)化代碼,可以使用if let 來簡(jiǎn)化一下:
fn main() {
let config_max = Some(3u8);
if let Some(max) = config_max {
println!("res {}", max)
}
}使用 if let 意味著編寫更少代碼,更少的縮進(jìn)和更少的樣板代碼。然而,這樣會(huì)失去 match 強(qiáng)制要求的窮盡性檢查。match 和 if let 之間的選擇依賴特定的環(huán)境以及增加簡(jiǎn)潔度和失去窮盡性檢查的權(quán)衡取舍。
換句話說,可以認(rèn)為 if let 是 match 的一個(gè)語法糖,它當(dāng)值匹配某一模式時(shí)執(zhí)行代碼而忽略所有其他值。
至于下環(huán)線匹配的模式,可以通過if let else 來實(shí)現(xiàn),如下所示:
fn main() {
let mut count = 0;
let config_max = Some(3u8);
if let Some(max) = config_max {
println!("res {}", max)
} else {
count += 1;
}
}到此這篇關(guān)于Rust 枚舉和模式匹配的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Rust 枚舉和模式匹配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何使用bindgen將C語言頭文件轉(zhuǎn)換為Rust接口代碼
這篇文章主要介紹了使用bindgen將C語言頭文件轉(zhuǎn)換為Rust接口代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
rust 如何使用 cargo-nextest 替代 cargo te
cargo-nextest 是新一代的rust測(cè)試程序,能夠極大提升測(cè)試性能,可以完全替代 cargo test 命令,這篇文章主要介紹了rust 如何使用 cargo-nextest 替代 cargo test,需要的朋友可以參考下2024-05-05

