iT邦幫忙

2022 iThome 鐵人賽

DAY 10
0

環境

OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0

Continue...

接續昨天的主題,enum在Rust可以像結構一樣可以實作相關方法,可以擁有不同的資料變體。
今天接下去本章的後半,主要是想了解Option<T>這個內建的泛型enum

Option<T>

先前的篇章提過一點點,Rust並沒有所謂的Null,如果要表現空值,會使用Option<T>,如果到Rust的標準函式庫的文件查看,可以看到裡面長這樣:

pub enum Option<T> {
    None,
    Some(T),
}

None可以把它視為Null,但他本身還是enumSome(T)也是enumT是在定義我們原本要處理的型別,例如以下範例:

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

上面有提到使用enummatch的話,需要每個case都處理到,但如果要配對的值,像是一些...整數該怎麼辦?其餘不用的不可能一個一個列出來
答:使用_

以下為範例

let dice: u8 = 2;
match dice {
    1 | 2 | 3 | 4 | 5 | 6 => print!("Dice number."),
    _ => print!("Not dice number."),
}

Reference


上一篇
[Rust] 枚舉(Enumeration)
下一篇
[Rust] 常見集合 - 動態陣列 (vector)
系列文
嘗試30天學「不」會Rust18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言