深入了解Rust中的枚舉和模式匹配
枚舉的定義
結構體可以將字段和數(shù)據(jù)聚合在一起,而枚舉可以將一個值成為一個集合之一。
定義一個 IpAddrKind 枚舉:
enum IpAddrKind { V4, V6, }
枚舉值
創(chuàng)建 IpAddrKind 兩個不同成員的實例:
let four = IpAddrKind::V4; let six = IpAddrKind::V6;
注意:枚舉的成員位于其標識符的命名空間中,并使用兩個冒號分開。
定義一個函數(shù)來獲取任何 IpAddrKind,可以使用任一成員來調用這個函數(shù):
fn route(ip_kind: IpAddrKind) {} route(IpAddrKind::V4); route(IpAddrKind::V6);
將數(shù)據(jù)直接放進每一個枚舉成員
將 IP 地址的數(shù)據(jù)和 IpAddrKind 成員存儲在一個 struct 中,關聯(lián)枚舉成員與值:
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"), };
可以使用一種更簡潔的方式來表達相同的概念,僅僅使用枚舉并將數(shù)據(jù)直接放進每一個枚舉成員而不是將枚舉作為結構體的一部分。
IpAddr 枚舉的新定義表明了 V4 和 V6 成員都關聯(lián)了 String 值:
enum IpAddr { V4(String), V6(String), } let home = IpAddr::V4(String::from("127.0.0.1")); let loopback = IpAddr::V6(String::from("::1"));
IpAddr::V4() 是一個獲取 String 參數(shù)并返回 IpAddr 類型實例的函數(shù)調用,這些構造函數(shù)會自動被定義。
將不同類型和數(shù)量的數(shù)據(jù)放入枚舉成員
用枚舉替代結構體還有另一個優(yōu)勢:每個成員可以處理不同類型和數(shù)量的數(shù)據(jù)。枚舉則可以輕易的處理這個情況:
enum IpAddr { V4(u8, u8, u8, u8), //V4 地址存儲為四個 u8 值 V6(String), //V6 地址存儲為一個 String } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1"));
存儲和編碼 IP 地址實在是太常見了,標準庫提供了一個開箱即用的定義:
struct Ipv4Addr { // --snip-- } struct Ipv6Addr { // --snip-- } enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), }
這說明可以將任意類型的數(shù)據(jù)放入枚舉成員中:例如字符串、數(shù)字類型或者結構體,甚至可以包含另一個枚舉。
有關聯(lián)值的枚舉和結構體的相似性
一個 Message 枚舉,其每個成員都存儲了不同數(shù)量和類型的值:
enum Message { Quit, //Quit 沒有關聯(lián)任何數(shù)據(jù) Move { x: i32, y: i32 }, //Move 類似結構體包含命名字段 Write(String), //Write 包含單獨一個 String ChangeColor(i32, i32, i32), //ChangeColor 包含三個 i32 }
如下結構體可以包含和上面枚舉成員相同的數(shù)據(jù),但它們都有不同的類型:
struct QuitMessage; // 類單元結構體 struct MoveMessage { x: i32, y: i32, } struct WriteMessage(String); // 元組結構體 struct ChangeColorMessage(i32, i32, i32); // 元組結構體
使用 impl 在枚舉上定義方法
可以使用 impl 在枚舉上定義方法,在 Message 枚舉上定義一個叫做 call 的方法:
impl Message { fn call(&self) { // 在這里定義方法體 } } let m = Message::Write(String::from("hello")); m.call();
方法體使用了 self 來獲取調用方法的值,上面的變量 m 就是當 m.call() 運行時 call 方法中的 self 的值。
Option 枚舉和其相對于空值的優(yōu)勢
Option 是標準庫定義的一個枚舉,它編碼了一個非常普遍的場景:一個值要么有值要么沒值。
Rust 沒有空值功能,空值(Null )是一個值,它代表沒有值。在有空值的語言中,變量總是這兩種狀態(tài)之一:空值和非空值。
Rust 并沒有空值,不過它確實擁有一個可以編碼存在或不存在概念的枚舉 Option<T>,而且它定義于標準庫中,如下:
enum Option<T> { None, Some(T), }
Option 枚舉包含在 prelude 之中不需要將其顯式引入作用域,它的成員也可以不需要 Option:: 前綴來直接使用 Some 和 None。
一些包含數(shù)字類型和字符串類型 Option 值的例子:
//根據(jù)Some 成員的值推斷變量類型 let some_number = Some(5); //some_number 的類型是 Option<i32> let some_char = Some('e'); //some_char 的類型是 Option<char> //需要顯示指定 Option 整體的類型 為 Option<i32> let absent_number: Option<i32> = None;
因為 Option 和 T(這里 T 可以是任何類型)是不同的類型,編譯器不允許像一個肯定有效的值那樣使用 Option:
代碼嘗試將 Option 與 i8 相加,無法通過編譯:
let x: i8 = 5; let y: Option<i8> = Some(5); //錯誤,無法通過編譯! let sum = x + y;
在對 Option 進行運算之前必須將其轉換為 T,這能幫助我們捕獲到空值最常見的問題之一:假設某值不為空但實際上為空的情況。
為了使用 Option 值,需要編寫處理每個成員的代碼。match 表達式就是這么一個處理枚舉的控制流結構:它會根據(jù)枚舉的成員運行不同的代碼,這些代碼可以使用匹配到的值中的數(shù)據(jù):
let some_value: Option<i32> = Some(42); match some_value { Some(value) => { println!("The value is: {}", value); // 在這里可以使用 value } None => { println!("The value is None"); // 處理 None 的情況 } }
match 控制流結構
Rust 有一個叫做 match 的極為強大的控制流運算符,它允許我們將一個值與一系列的模式相比較,并根據(jù)相匹配的模式執(zhí)行相應代碼。
注:模式可由字面值、變量、通配符和許多其他內容構成。
編寫一個函數(shù)來獲取一個未知的硬幣,并以一種類似驗鈔機的方式,確定它是何種硬幣并返回它的美分值:
enum Coin { Penny, Nickel, Dime, Quarter, } fn value_in_cents(coin: Coin) -> u8 { match coin { //如果想要在分支中運行多行代碼,可以使用大括號,而分支后的逗號是可選的 Coin::Penny => { println!("Lucky penny!"); 1 } //果分支代碼較短的話通常不使用大括號 Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, } }
每個分支相關聯(lián)的代碼是一個表達式,而表達式的結果值將作為整個 match 表達式的返回值。
綁定值的模式
匹配分支的另一個有用的功能是可以綁定匹配的模式的部分值,這也就是如何從枚舉成員中提取值的。
改變 Quarter 成員來包含一個 State 值:
#[derive(Debug)] // 這樣可以立刻看到州的名稱 enum UsState { Alabama, Alaska, // --snip-- } enum Coin { Penny, Nickel, Dime, Quarter(UsState), }
在匹配 Coin::Quarter 成員的分支的模式中增加了一個叫做 state 的變量,當匹配到 Coin::Quarter 時變量 state 將會綁定對應州的值:
fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter(state) => { println!("State quarter from {:?}!", state); 25 } } }
匹配 Option<T>
在 Option<i32> 上使用 match 表達式的函數(shù):
fn plus_one(x: Option<i32>) -> Option<i32> { match x { None => None, //i 綁定了 Some 中包含的值 Some(i) => Some(i + 1), } } let five = Some(5); let six = plus_one(five); let none = plus_one(None);
將 match 與枚舉相結合在很多場景中都是有用的,Rust 代碼中有很多這樣的模式:match 一個枚舉,綁定其中的值到一個變量,接著根據(jù)其值執(zhí)行代碼。
匹配是窮盡的
以下代碼沒有處理 None 的情況,無法通過編譯:
fn plus_one(x: Option<i32>) -> Option<i32> { match x { Some(i) => Some(i + 1), } }
Rust 中的匹配是 窮盡的(exhaustive):必須窮舉到最后的可能性來使代碼有效。
通配模式和 _ 占位符
對一些特定的值采取特殊操作,而對其他的值采取默認操作,模式 other 涵蓋了所有其他可能的值:
let dice_roll = 9; match dice_roll { 3 => add_fancy_hat(), 7 => remove_fancy_hat(), other => move_player(other), } fn add_fancy_hat() {} fn remove_fancy_hat() {} fn move_player(num_spaces: u8) {}
當不想使用通配模式獲取的值時,請使用 _ ,這是一個特殊的模式,可以匹配任意值而不綁定到該值:
let dice_roll = 9; match dice_roll { 3 => add_fancy_hat(), 7 => remove_fancy_hat(), _ => reroll(), //或者 _ => (), } fn add_fancy_hat() {} fn remove_fancy_hat() {} fn reroll() {}
if let 簡潔控制流
可以認為 if let 是 match 的一個語法糖,它當值匹配某一模式時執(zhí)行代碼而忽略所有其他值。
match 只關心當值為 Some 時執(zhí)行代碼:
let config_max = Some(3u8); match config_max { Some(max) => println!("The maximum is configured to be {}", max), _ => (), }
可以使用 if let 這種更短的方式編寫:
let config_max = Some(3u8); if let Some(max) = config_max { println!("The maximum is configured to be {}", max); }
可以在 if let 中包含一個 else。else 塊中的代碼與 match 表達式中的 _ 分支塊中的代碼相同,這樣的 match 表達式就等同于 if let 和 else。
使用 match 表達式:
let mut count = 0; match coin { Coin::Quarter(state) => println!("State quarter from {:?}!", state), _ => count += 1, }
使用 if let 和 else 表達式:
let mut count = 0; if let Coin::Quarter(state) = coin { println!("State quarter from {:?}!", state); } else { count += 1; }
以上就是深入了解Rust中的枚舉和模式匹配的詳細內容,更多關于Rust枚舉和模式匹配的資料請關注腳本之家其它相關文章!
相關文章
使用win10 wsl子系統(tǒng)如何將 rust 程序靜態(tài)編譯為linux可執(zhí)行文件
這篇文章主要介紹了使用win10 wsl子系統(tǒng)如何將 rust 程序靜態(tài)編譯為linux可執(zhí)行文件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2025-05-05