1. 為什麼需要 Result
在 C/C++ 中,函數失敗時可能回傳 -1 或 NULL,但這種方式不直觀,也容易忘記檢查,Rust 提供 Result,明確表示可能成功 (Ok)或可能失敗 (Err)。
enum Result<T, E> {
Ok(T),
Err(E),
}
2. 基本範例
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(a / b)
}
}
fn main() {
let r1 = divide(10, 2);
let r2 = divide(10, 0);
println!("r1 = {:?}", r1);
println!("r2 = {:?}", r2);
}
輸出:
r1 = Ok(5)
r2 = Err("Cannot divide by zero")
3. 使用 match 處理 Result
fn main() {
let result = divide(10, 0);
match result {
Ok(value) => println!("Result = {}", value),
Err(msg) => println!("Error: {}", msg),
}
}
輸出:
Error: Cannot divide by zero
4. unwrap 與 expect
如果確定一定會成功,可以用 unwrap() 或 expect() 直接取值,程式錯誤時會 panic。
fn main() {
let r = divide(10, 2).unwrap();
println!("Result = {}", r);
let r2 = divide(10, 0).expect("Division failed");
println!("Result = {}", r2);
}
輸出(第二行會直接 panic):
Result = 5
thread 'main' panicked at 'Division failed: "Cannot divide by zero"'
5. ? 運算子
? 可以簡化錯誤傳遞,把錯誤往上丟。
use std::fs::File;
fn open_file() -> std::io::Result<File> {
let f = File::open("hello.txt")?; // 如果失敗,直接回傳 Err
Ok(f)
}
這樣就不用一層層寫 match 來傳遞錯誤。
6. 學習心得與補充
今天學到的 Result 讓我覺得 Rust 在錯誤處理上比我以前寫過的語言更嚴謹。C++ 或 C 常用錯誤碼來代表失敗,但如果沒檢查就會直接出錯,Rust 則是用 Result 強迫我面對錯誤情況,不可能忽略。搭配 match 能寫得很完整,而 unwrap、expect 在我很確定的情況下又能省事,? 運算子讓錯誤傳遞變得簡潔。這讓我想到前幾天學的 Option,我覺得 Result 就像是更進一步把有沒有值擴充成成功或失敗,雖然覺得每次都要處理 Ok 和 Err 有點麻煩,但這樣的檢查其實會讓寫程式時更安心。