本章介紹?枚舉(enumerations),也被稱作?enums。枚舉允許你通過列舉可能的?成員(variants) 來定義一個類型。首先,我們會定義并使用一個枚舉來展示它是如何連同數據一起編碼信息的。接下來,我們會探索一個特別有用的枚舉,叫做?Option
,它代表一個值要么是某個值要么什么都不是。然后會講到在?match
?表達式中用模式匹配,針對不同的枚舉值編寫相應要執(zhí)行的代碼。最后會介紹?if let
,另一個簡潔方便處理代碼中枚舉的結構。
6.1?定義枚舉
定義一個枚舉
fn main() {
}
enum IpAddrKind {
V4,
V6,
}
現在?IpAddrKind
?就是一個可以在代碼中使用的自定義數據類型了。
枚舉值
可以像這樣創(chuàng)建?IpAddrKind
?兩個不同成員的實例:
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
注意枚舉的成員位于其標識符的命名空間中,并使用兩個冒號分開。這么設計的益處是現在?IpAddrKind::V4
?和?IpAddrKind::V6
?都是?IpAddrKind
?類型的。
一個案例:
fn main() {
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
}
這里定義了一個有兩個字段的結構體?IpAddr
:IpAddrKind
(之前定義的枚舉)類型的?kind
?字段和?String
?類型?address
?字段。有這個結構體的兩個實例。第一個,home
,它的?kind
?的值是?IpAddrKind::V4
?與之相關聯的地址數據是?127.0.0.1
。第二個實例,loopback
,kind
?的值是?IpAddrKind
?的另一個成員,V6
,關聯的地址是?::1
。使用了一個結構體將?kind
?和?address
?打包在一起,現在枚舉成員就與值相關聯了。
可以使用一種更簡潔的方式來表達相同的概念,僅僅使用枚舉并將數據直接放進每一個枚舉成員而不是將枚舉作為結構體的一部分。
fn main() {
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}
用枚舉替代結構體還有另一個優(yōu)勢:每個成員可以處理不同類型和數量的數據。IPv4 版本的 IP 地址總是含有四個值在 0 和 255 之間的數字部分。如果我們想要將?V4
?地址存儲為四個?u8
?值而?V6
?地址仍然表現為一個?String
,這就不能使用結構體了。
fn main() {
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
}
結構體和枚舉還有另一個相似點:就像可以使用?impl
?來為結構體定義方法那樣,也可以在枚舉上定義方法。
fn main() {
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
// 在這里定義方法體
}
}
let m = Message::Write(String::from("hello"));
m.call();
}
方法體使用了?self
?來獲取調用方法的值。這個例子中,創(chuàng)建了一個值為?Message::Write(String::from("hello"))
?的變量?m
,而且這就是當?m.call()
?運行時?call
?方法中的?self
?的值。
Option 枚舉和其相對于空值的優(yōu)勢
Option
?是標準庫定義的另一個枚舉。Option
?類型應用廣泛因為它編碼了一個非常普遍的場景,即一個值要么有值要么沒值。從類型系統(tǒng)的角度來表達這個概念就意味著編譯器需要檢查是否處理了所有應該處理的情況,這樣就可以避免在其他編程語言中非常常見的 bug。
Rust 并沒有很多其他語言中有的空值功能。空值(Null?)是一個值,它代表沒有值。在有空值的語言中,變量總是這兩種狀態(tài)之一:空值和非空值。
Rust 并沒有空值,不過它確實擁有一個可以編碼存在或不存在概念的枚舉。這個枚舉是 Option<T>,而且它定義于標準庫中,如下:
fn main() {
enum Option<T> {
Some(T),
None,
}
}
Option<T>
?枚舉是如此有用以至于它甚至被包含在了 prelude 之中,你不需要將其顯式引入作用域。另外,它的成員也是如此,可以不需要?Option::
?前綴來直接使用?Some
?和?None
。即便如此?Option<T>
?也仍是常規(guī)的枚舉,Some(T)
?和?None
?仍是?Option<T>
?的成員。
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number:Option<i32> = None;
println!("{:#?}, {:#?}, {:#?} ", some_number, some_string, absent_number);
}
結果
如果使用?None
?而不是?Some
,需要告訴 Rust?Option<T>
?是什么類型的,因為編譯器只通過?None
?值無法推斷出?Some
?成員保存的值的類型。
簡而言之,因為?Option<T>
?和?T
(這里?T
?可以是任何類型)是不同的類型,編譯器不允許像一個肯定有效的值那樣使用?Option<T>
。
fn main() {
let x : i32 = 8;
let y:Option<i32> = Some(10);
let sum = x + y;
}
結果
?錯誤信息意味著 Rust 不知道該如何將?Option<i8>
?與?i8
?相加,因為它們的類型不同。
6.2?match控制流運算符
match
?的力量來源于模式的表現力以及編譯器檢查,它確保了所有可能的情況都得到處理。
一個案例:
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
// 使用了match
fn value_in_cents(coin : Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter =>25,
}
}
fn main() {
let data = Coin::Nickel;
println!("{}", value_in_cents(data));
let data = Coin::Penny;
println!("{}", value_in_cents(data));
let data = Coin::Dime;
println!("{}", value_in_cents(data));
let data = Coin::Quarter;
println!("{}", value_in_cents(data));
}
結果
拆開?value_in_cents
?函數中的?match
?來看。首先,我們列出?match
?關鍵字后跟一個表達式,在這個例子中是?coin
?的值。這看起來非常像?if
?使用的表達式,不過這里有一個非常大的區(qū)別:對于?if
,表達式必須返回一個布爾值,而這里它可以是任何類型的。
接下來是?match
?的分支。一個分支有兩個部分:一個模式和一些代碼。第一個分支的模式是值?Coin::Penny
?而之后的?=>
?運算符將模式和將要運行的代碼分開。這里的代碼就僅僅是值?1
。每一個分支之間使用逗號分隔。
當?match
?表達式執(zhí)行時,它將結果值按順序與每一個分支的模式相比較。如果模式匹配了這個值,這個模式相關聯的代碼將被執(zhí)行。如果模式并不匹配這個值,將繼續(xù)執(zhí)行下一個分支,非常類似一個硬幣分類器。
每個分支相關聯的代碼是一個表達式,而表達式的結果值將作為整個?match
?表達式的返回值。
綁定值的模式
匹配分支的另一個有用的功能是可以綁定匹配的模式的部分值。這也就是如何從枚舉成員中提取值的。
一個案例:
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin : Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quater from {:?}!", state);
25
},
}
}
fn main() {
let data = Coin::Quarter(UsState::Alaska);
println!("{}", value_in_cents(data));
}
結果
如果調用?value_in_cents(Coin::Quarter(UsState::Alaska))
,coin
?將是?Coin::Quarter(UsState::Alaska)
。當將值與每個分支相比較時,沒有分支會匹配,直到遇到?Coin::Quarter(state)
。這時,state
?綁定的將會是值?UsState::Alaska
。接著就可以在?println!
?表達式中使用這個綁定了,像這樣就可以獲取?Coin
?枚舉的?Quarter
?成員中內部的州的值。
匹配Option<T>
編寫一個函數,它獲取一個?Option<i32>
?并且如果其中有一個值,將其加一。如果其中沒有值,函數應該返回?None
?值并不嘗試執(zhí)行任何操作。
fn main() {
let five = Some(5);
let five = plus_one(five);
let six = plus_one(five);
let none = plus_one(None);
println!("{:?}, {:?}, {:?}", five, six, none);
}
fn plus_one(x : Option<i32>) -> Option<i32> {
match x {
None => None,
Some(x) => Some(x + 1),
}
}
結果
匹配上了就返回,匹配不上就會陷入死循環(huán),rust會報錯。
?_通配符
Rust 也提供了一個模式用于不想列舉出所有可能值的場景。例如,u8
?可以擁有 0 到 255 的有效的值,如果我們只關心 1、3、5 和 7 這幾個值,就并不想必須列出 0、2、4、6、8、9 一直到 255 的值。所幸不必這么做:可以使用特殊的模式?_
?替代:
fn main() {
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => println!("emo")
}
}
?6.3?if let簡潔控制流
if let
?語法讓我們以一種不那么冗長的方式結合?if
?和?let
,來處理只匹配一個模式的值而忽略其他模式的情況。
fn main() {
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ =>(),
}
}
我們想要對?Some(3)
?匹配進行操作但是不想處理任何其他?Some<u8>
?值或?None
?值。
可以使用?if let
?這種更短的方式編寫
fn main() {
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
println!("three");
}
}
if let
?獲取通過等號分隔的一個模式和一個表達式。它的工作方式與?match
?相同,這里的表達式對應?match
?而模式則對應第一個分支。
可以認為?if let
?是?match
?的一個語法糖,它當值匹配某一模式時執(zhí)行代碼而忽略所有其他值。
可以在?if let
?中包含一個?else
。else
?塊中的代碼與?match
?表達式中的?_
?分支塊中的代碼相同,這樣的?match
?表達式就等同于?if let
?和?else
。文章來源:http://www.zghlxwxcb.cn/news/detail-640706.html
參考:枚舉與模式匹配 - Rust 程序設計語言 簡體中文版 (bootcss.com)文章來源地址http://www.zghlxwxcb.cn/news/detail-640706.html
到了這里,關于【Rust】Rust學習 第六章枚舉和模式匹配的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!