枚舉 enum,用于從眾多選項(xiàng)中選擇一個。
定義枚舉
#[derive(Debug)]
enum Week {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
fn main() {
let today = Week::Saturday; // 使用枚舉
let tomorrow = Week::Sunday;
println!("{:?}", today);
}
這是我們在很多面向?qū)ο笳Z言中常見的定義枚舉的方式
以往對枚舉的認(rèn)識,就是枚舉限定了幾個固定的選項(xiàng),我們只能使用眾多選項(xiàng)中的一個,或者說,只是使用了某一個元素的名字,例如上面Week枚舉中的Monday,至于Monday是什么,無所謂,Monday = 1也好,Monday = 10也好,我們并不關(guān)心。但是對于Rust,我們對枚舉有更進(jìn)一步的應(yīng)用。
用枚舉代替結(jié)構(gòu)體
#[derive(Debug)]
enum IpAddr {
V4(u8, u8, u8, u8), // 這個枚舉成員是四個u8類型的元祖
V6(String), // 這個枚舉成員是String類型
}
fn main() {
// 定義一個枚舉變量,并將四個值存放
let loopbackV4 = IpAddr::V4(127, 0, 0, 1);
// 定義另一個枚舉變量,存入一個 String 類型的值
// "xxx".to_string() 方法和 String::from("xxx") 是一樣的效果
let loopbackV6 = IpAddr::V6("::1".to_string());
println!("{:?}\n{:?}", loopbackV4, loopbackV6);
}
上面的代碼,我們可以給枚舉的每一個成員,指定一個數(shù)據(jù)類型,并且在創(chuàng)建一個枚舉變量的時候,將某個值存入枚舉。在 struct 中可以存儲不同類型的變量,現(xiàn)在,在枚舉中也可以。
再來看一個例子
// 定義一個操作枚舉
#[derive(Debug)]
enum Operation {
Move {x: i32, y:i32},
Jump(u32),
Attack(i32),
}
fn main() {
// 定義一個移動的操作
let opt_move = Operation::Move {x: 10, y: 11};
// 定義一個攻擊的操作
let opt_attack = Operation::Attack(100);
// 定義一個跳躍的操作
let opt_jump = Operation::Jump(3);
DoOperation(opt_move);
DoOperation(opt_attack);
DoOperation(opt_jump);
}
// 執(zhí)行操作
fn DoOperation(opt: Operation) {
println!("Do operation: {:?}", opt);
}
上面的代碼我們定義了一個 Operation 枚舉,里面有移動,攻擊和跳躍三種操作方式。在 main 中定義了三個操作的變量,并且將每一次操作的具體值直接附加到了枚舉成員上,例如 opt_attack 攻擊操作,這次操作的傷害是100。
Rust 中使用
enum代替struct將獲得更簡潔的代碼。并且,每個枚舉成員可以處理不同類型和數(shù)量的數(shù)據(jù)。
Rust 的 Option 枚舉解釋
Rust 中沒有 Null 值,無法將一個變量賦值為 Null, 例如 let a = Null;,這樣的操作在Rust中不存在。但是Rust中有 Option 枚舉,這個枚舉,用于表示 存在 與 不存在 的概念。有點(diǎn)抽象,沒關(guān)系,一步一步來,先看下 Option 源代碼的定義
enum Option<T> {
Some(T),
None,
}
這里的 <T> 是指可以代表任何數(shù)據(jù)類型的,這是范型相關(guān)的東西,后面會學(xué)習(xí)??梢詫?Option 枚舉想象成可以裝不同類型東西的小盒子,例如我們定義了一個裝玩具汽車的小盒子,這個小盒子里只能裝玩具汽車。任何時候,只要這個盒子存在,那么里面就會有兩種狀態(tài),要么有玩具汽車,要么沒有玩具汽車。在有些面向?qū)ο蟮恼Z言中,如果訪問一個玩具汽車,而恰好當(dāng)時那里沒有玩具汽車,那么就會造成空引用,如果沒有手動處理空引用的情況,則程序就會出現(xiàn)Bug。而Rust則避免了 空引用 的情況。
看下面的代碼
fn main(){
// 使用 Option 將一個 String 類型的值包起來
let name: Option<String> = Some("Fred".to_string());
}
Option用于某些地方可能存在有值或沒值的情況。Option 及成員已經(jīng)被自動包含,所以我們不需要Option::Some(xxx)這樣來使用。
match 匹配
對于 enum 類型的值,我們不能直接比較,看下面的代碼,是無法編譯通過的。
let name: Option<String> = Option::Some("Jack".to_string());
println!(name == "Jack".to_string());
上面代碼中 name == "Jack".to_string() 編譯出錯,因?yàn)?== 兩邊的數(shù)據(jù)類型不一樣。這里,我們就可以用到 match。
看下面的代碼
#[derive(Debug)]
enum Operation {
Move {x: i32, y:i32},
Jump(u32),
Attack(i32),
Talk(String),
}
fn main() {
let opt_talk = Operation::Talk("Hello".to_string());
let opt_move = Operation::Move { x: 10, y: 20 };
match opt_move {
Operation::Talk(ref value) => { // 這里加了 ref 是為了避免所有權(quán)轉(zhuǎn)移
println!("Talk: {:?}", value);
},
Operation::Move {x,y} => {
println!("Move, x: {}, y: {}", x, y);
}
_ => {
// nothing
}
}
}
上面的代碼中,match Operation 枚舉時,并沒有匹配所有的情況,所以最后需要 _ => ,相當(dāng)于某些編譯語言中 switch 中的 default,即在上面的情況都不匹配的情況下,執(zhí)行的操作。
if let 使用
直接看代碼,簡化上面 match 的操作
if let Operation::Move{x, y} = opt_move {
println!("Move, x: {}, y: {}", x, y);
} else {
println!("nothing");
}
這樣就可以不用 match 直接匹配枚舉中的某一個成員類型。
感覺這篇博客有些地方寫的可能不是很清楚,說明我對這塊知識的理解程度還不夠。下面是一些講解 Rust 枚舉的鏈接
https://rustwiki.org/zh-CN/rust-by-example/custom_types/enum.html
https://www.twle.cn/c/yufei/rust/rust-basic-enums.html
http://www.ameyalokare.com/rust/2017/10/23/rust-options.html