Day 3:使用 std::error::Error trait

學習目標

  • 認識 std::error::Error trait
  • 用 Box 當泛用錯誤型別
  • 用 map_err 加上下文資訊

今日重點

Error trait

std::error::Error 是 Rust 標準錯誤的標記 trait。任何實作它的型別都可被當作「錯誤」。用 Box<dyn Error> 能容納不同型別的錯誤。

Box

use std::fs;

fn read_and_process(path: &str) -> Result<String, Box<dyn std::error::Error>> {
    let contents = fs::read_to_string(path)?;   // 自動轉成 Box<dyn Error>
    Ok(contents.to_uppercase())
}

Box<dyn Error> 缺點:失去具體型別,無法 match 特定錯誤。適合「只想向上拋、不細分」的場合,或簡潔的 main。

map_err + 上下文

fn process_data(path: &str) -> Result<String, Box<dyn std::error::Error>> {
    let contents = fs::read_to_string(path)
        .map_err(|e| format!("Failed to read {}: {}", path, e))?;
    // 轉成 String 失去原錯誤型別,但保有可讀訊息
    Ok(contents)
}

若要「帶 context 但保留原型別」,實務常用 anyhow::Context(第 10 週)。

練習

use std::fs;

// 1. 寫一個函式回傳 Result<u64, Box<dyn Error>>:讀檔後 parse 成數字
fn parse_number_file(path: &str) -> Result<u64, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(path)?;
    let n: u64 = content.trim().parse()?;   // 注意 String -> u64
    Ok(n)
}

// 2. 在 main 中用 match 或 expect 呼叫
fn main() {
    match parse_number_file("num.txt") {
        Ok(n) => println!("Number: {}", n),
        Err(e) => eprintln!("Error: {}", e),
    }
}

自我檢查

  • 知道 Error trait 是標準的標記
  • 會用 Box 容納多種錯誤
  • 用 map_err 提供上下文
  • 知道 Box 的取捨(失去具體型別)

深入連結

  • The Book 第 9 章「Taking Decisions on Error Handling」