Option<T> 與空值安全(Option<T> and null safety)
在 C 裡,極多錯誤都可追溯到一個值:NULL。指標可以是 NULL,用來表示「這裡什麼也沒有」、「沒有結果」、「找不到」——而危險在於,NULL 看起來和有效指標一模一樣,直到你解參考它,那一刻你的程式就以記憶體區段錯誤當機。麻煩在於型別 char *p 對 p 是否可能為 NULL 隻字未提,所以語言從不提醒你去檢查,而漏掉一次檢查就足以釀禍。Rust 徹底移除了 NULL。在安全的 Rust 裡沒有空指標。取而代之,「可能不存在」這件事被明確地寫進型別裡,用 Option<T>。
Option<T> 是一個要嘛裝著一個值、要嘛什麼也不裝的型別,而你必須講明是哪一種。它剛好有兩種形狀:Some(x),表示有一個值、它是 x;以及 None,表示沒有值。所以在 C 函式回傳一個可能為 NULL 的 char * 之處,Rust 函式回傳 Option<String>——現在型別本身就宣告了「這可能不存在」。關鍵的後果是,你不可能不小心把這個值當成它永遠都在那樣使用。要取得內部的值,你必須處理兩種情形,通常用 match,或用像 if let Some(x) = opt 的輔助寫法。編譯器不會讓你在還沒承認 None 的可能性之前就碰到 Some(x) 裡的 x,於是「漏掉 NULL 檢查」這個錯誤變成了編譯錯誤,而非執行期當機。
為何重要:值的不存在現在由型別系統追蹤,而非交給你可能忘掉的慣例。若一個函式可能什麼都不回傳,它的回傳型別就寫 Option,而每個呼叫者都被編譯器強迫去處理「什麼都沒有」的情形。這把 C 最常見的一整類當機——空指標解參考——轉成了在程式跑起來之前就被抓到的錯誤。誠實的提醒是:Rust 確實給了你像 opt.unwrap() 這樣的逃生口,它取出值,並在值為 None 時刻意恐慌(panic,以受控方式當機)。unwrap 用於快速程式碼與不可能發生的情形是沒問題的,但為了讓編譯器閉嘴而到處用它,就等於把 Option 本想提供的那份安全給扔掉了。
fn first_char(s: &str) -> Option<char> { s.chars().next() // 非空則 Some(c),否則 None } match first_char("hi") { Some(c) => println!("第一個字是 {c}"), None => println!("空字串"), }
型別表明值可能不存在;編譯器逼你處理 None 的情形。
Option 不是讓「不存在」變得不可能——它是讓「不存在」變得無法忽視。opt.unwrap() 會把值取出,但遇到 None 就恐慌;單純為了讓編譯器安靜而動用 unwrap,等於丟掉了 Option 存在的目的所給的保護。