OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
接續昨天的主題,enum
在Rust可以像結構一樣可以實作相關方法,可以擁有不同的資料變體。
今天接下去本章的後半,主要是想了解Option<T>
這個內建的泛型enum。
Option<T>
先前的篇章提過一點點,Rust並沒有所謂的Null,如果要表現空值,會使用Option<T>
,如果到Rust的標準函式庫的文件查看,可以看到裡面長這樣:
pub enum Option<T> {
None,
Some(T),
}
None
可以把它視為Null
,但他本身還是enum
。Some(T)
也是enum
,T
是在定義我們原本要處理的型別,例如以下範例:
struct Name {
first_name: String,
middle_name: Option<String>, // middle_name can be empty
last_name: String,
}
這意味著,middle_name
可能是None
或是Some(String)
,先回到原本的描述,這跟Rust用Option<T>
取代Null
,有甚麼關係呢?
我的理解是:
我們知道Null
麻煩的地方在於,在意想不到的某個質變成了空值或是以為是空值,然後沒處理到,所以大多情況都會加個:
if (val == nullptr) {
// do something if val is null
} else {
// do something if val is NOT null
}
這樣的處理。換句話說,沒有處理到所有可能的情況。
Rust,這門問世就標榜安全唯一大特性的語言,把很多問題留在編譯期就提早發現提早解決了,例如之前的提過的所有權(ownership),就用變數的借用(borrow)或轉移(move)的方式把問題限制住了。
Rust中的enum
也有些限制,這邊提一下,有了enum
就不得不說switch..case
了(C語言家族),在Rust裡面叫做match
,以昨天的Action
為例:
struct Skill {
id: i32,
atk: f32,
}
enum Action {
Walk { x: i32, y: i32 },
Attack(Skill),
Jump(f32),
}
impl Action {
fn print(&self) {
match self {
Action::Walk { x, y } => println!("Walk to position: ({}, {})", x, y),
Action::Attack(skill) => println!(
"Attack with skill: {}, attack damage: {}",
skill.id, skill.atk
),
Action::Jump(height) => println!("Jump height is {} meter(s)", height),
}
}
}
如果把print
中的Action::Jump(f32)
項目拿掉,Compiler會開始生氣:
Compiling basic v0.1.0 (D:\projects\rust_learning\basic)
error[E0004]: non-exhaustive patterns: `&Jump(_)` not covered
--> src\main.rs:14:15
|
14 | match self {
| ^^^^ pattern `&Jump(_)` not covered
|
note: `Action` defined here
--> src\main.rs:9:5
|
6 | enum Action {
| ------
...
9 | Jump(f32),
| ^^^^ not covered
= note: the matched value is of type `&Action`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
19 ~ ),
20 ~ &Jump(_) => todo!(),
|
For more information about this error, try `rustc --explain E0004`.
他會要求你要實作Action::Jump(f32)
的處理,這樣就可以知道了,如果使用Option<T>
,Rust compiler可以在編譯期要求你把有值跟沒有值的情況都進行處理。
Option<T>
內容這是一個補充,假如使用Option<T>
,如果有值,怎麼把包在Some(T)
裡面的值取出來呢?
答:使用unwrap()
。
以下為範例
let x: u8 = 3;
let y: Option<u8> = Some(4);
let ans = x + y.unwrap();
println!("Answer is {}", ans);
match
上面有提到使用enum
跟match
的話,需要每個case都處理到,但如果要配對的值,像是一些...整數該怎麼辦?其餘不用的不可能一個一個列出來
答:使用_
。
以下為範例
let dice: u8 = 2;
match dice {
1 | 2 | 3 | 4 | 5 | 6 => print!("Dice number."),
_ => print!("Not dice number."),
}