Skip to content

建立 2026-09-19 更新 2026-09-19

錯誤處理

Rust 沒有例外(exception)。它把錯誤分成兩類:無法恢復的錯誤用 panic! 中止程式,可以恢復的錯誤則用 Result<T, E> 這個回傳型別表示。因為錯誤是型別的一部分,編譯器會提醒你不能假裝它不存在。

無法恢復:panic!

panic! 會印出訊息並終止程式(預設會展開堆疊並清理資源)。適用於「程式出現了不應該發生的 bug」,例如陣列索引越界:

fn main() {
    // 💥 執行時 panic:索引越界
    let v = vec![1, 2, 3];
    println!("{}", v[99]);
}

一般的錯誤(檔案不存在、輸入格式不對、網路失敗)不該用 panic,而要用 Result

可恢復:Result

Result 是標準函式庫的列舉,有兩個變體:

enum Result<T, E> {
    Ok(T),   // 成功,帶著結果
    Err(E),  // 失敗,帶著錯誤資訊
}

許多標準函式庫的操作都回傳 Result。例如把字串轉成數字:

fn main() {
    let inputs = ["42", "abc", "-7"];

    for text in inputs {
        match text.parse::<i32>() {
            Ok(n) => println!("{text} → 成功:{n}"),
            Err(e) => println!("{text} → 失敗:{e}"),
        }
    }
}

unwrap 與 expect

unwrap() 遇到 ErrNone 會直接 panic;expect("訊息") 一樣會 panic,但能附上說明。它們適合用在範例、測試,或你確定不可能失敗的地方,正式程式碼盡量避免:

fn main() {
    let n: i32 = "123".parse().expect("這是寫死的數字,一定能解析");
    println!("{n}");

    // 需要預設值時,用 unwrap_or 更安全
    let m: i32 = "abc".parse().unwrap_or(0);
    println!("{m}");
}

? 運算子:往上傳遞錯誤

函式內若某一步可能失敗,不想自己處理,就用 ? 把錯誤交給呼叫者。它的行為是:如果是 Ok,取出裡面的值繼續;如果是 Err,立刻從函式回傳這個錯誤:

use std::num::ParseIntError;

fn parse_and_double(text: &str) -> Result<i32, ParseIntError> {
    let n = text.parse::<i32>()?; // 失敗時直接回傳 Err
    Ok(n * 2)
}

fn main() {
    println!("{:?}", parse_and_double("21"));
    println!("{:?}", parse_and_double("x"));
}

? 只能用在回傳型別為 Result(或 Option)的函式中。main 也可以回傳 Result

use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let n: i32 = "100".parse()?;
    println!("讀到 {n}");
    Ok(())
}

Box<dyn Error> 可以裝下任何實作了 Error trait 的錯誤型別,寫小工具時很方便。

自訂錯誤型別

函式可能出現多種不同的錯誤時,可以自己定義一個 enum,並用 From? 自動轉換:

use std::fmt;
use std::num::ParseIntError;

#[derive(Debug)]
enum AppError {
    Parse(ParseIntError),
    Negative(i32),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::Parse(e) => write!(f, "解析失敗:{e}"),
            AppError::Negative(n) => write!(f, "不允許負數:{n}"),
        }
    }
}

impl From<ParseIntError> for AppError {
    fn from(e: ParseIntError) -> Self {
        AppError::Parse(e)
    }
}

fn parse_positive(text: &str) -> Result<i32, AppError> {
    let n: i32 = text.parse()?; // ParseIntError 會自動轉成 AppError
    if n < 0 {
        return Err(AppError::Negative(n));
    }
    Ok(n)
}

fn main() {
    for text in ["5", "-3", "abc"] {
        match parse_positive(text) {
            Ok(n) => println!("{n}"),
            Err(e) => println!("錯誤:{e}"),
        }
    }
}

實務上常用的套件

自己寫這些樣板程式碼很繁瑣。實務上,函式庫常用 thiserror 簡化自訂錯誤,應用程式常用 anyhow 統一處理錯誤。先學會本頁的基本觀念,再使用這些套件會容易許多。

Option 的常用技巧

Option 也支援 ?,並有一組好用的方法:

fn first_char_upper(s: &str) -> Option<char> {
    let c = s.chars().next()?; // 空字串時直接回傳 None
    Some(c.to_ascii_uppercase())
}

fn main() {
    println!("{:?}", first_char_upper("rust"));
    println!("{:?}", first_char_upper(""));

    let name: Option<&str> = None;
    println!("{}", name.unwrap_or("匿名"));

    let len = Some("hello").map(|s| s.len());
    println!("{:?}", len);

    // 把 Option 轉成 Result
    let r: Result<i32, &str> = None.ok_or("沒有值");
    println!("{:?}", r);
}

該用哪一種?

情況 做法
程式 bug、不可能發生的狀態 panic!unwrapexpect
可能失敗的操作 回傳 Result
可能沒有值,但不算錯誤 回傳 Option
想把錯誤交給呼叫者 ?

推薦影音

錯誤處理

簡述:微軟《Beginner's Series to Rust》第 23 集,介紹 ResultOption 與錯誤處理的基本做法。

錯誤處理完整講解

簡述:Let's Get Rusty 依照官方書籍第 9 章製作,涵蓋 panic!Result? 運算子。

下一頁:閉包與迭代器