Rust 學習筆記:錯誤處理

2023年10月15日 · 中文


前言

本系列文書寫我個人初探 Rust 的學習筆記,章節劃分主要基於著名的 The BookThe Rust Programming Language,程式碼部分通常是個人閱讀消化後以方便說明的方式撰寫,完整學習建議直接參見該書。

The Rust Programming Language

該書也有中文翻譯版,不過個人閱讀以英文原版為主以鞏固對 terminology 的一致認識,我認為對未來閱讀以及查找資料會較為順暢。

不論語言該書都是相當優秀的學習資源,選擇適合你的語言開始學習 Rust 吧。

Appendix F: Translations of the Book

錯誤處理

許多語言中處理錯誤是用 try / catch 或 except 這樣例外處理的方式,Rust 則使用相當不同的處理機制。

panic

使用 panic macro 拋出一個錯誤,就如其名造成程式的「恐慌」離開程式。panic 適用於處理無法復原的錯誤。

fn main() {
    panic!("This is a error.");

    // thread 'main' panicked at src/main.rs:2:5:
    // This is a error.
}

Result

使用 Result enum 處理可被復原的錯誤,在可能會出錯的方法中經常使用的回傳型別,比如說開啟檔案。

下面嘗試開啟一個不存在的檔案,File::open 回傳的 Result 有兩種 variant,OkErrErr 便是代表錯誤。

use std::fs::File;

fn main() {
    let file = File::open("not-exist.txt");
    
    match file {
        Ok(file) => file,
        Err(err) => panic!("{:?}", err),
    };

    // Os { code: 2, kind: NotFound, message: "No such file or directory" }
}

unwrap

unwrap 方法會直接在錯誤時 panic,是一種快速的寫法。

let file = File::open("not-exist.txt").unwrap();
// called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }

expect

expect 則可以讓我們自訂錯誤訊息。

let file = File::open("not-exist.txt").expect("File not found");
// File not found: Os { code: 2, kind: NotFound, message: "No such file or directory" }

回傳錯誤

我們使用 Result 作為可能錯誤的方法的回傳值,這邊利用 ? 運算子會在 Err 時直接 return 錯誤。

use std::fs::File;
use std::io;

fn main() {
    let file = open_file("not-exist.txt");
    
    match file {
        Ok(file) => file,
        Err(err) => panic!("{:?}", err),
    };

    // Os { code: 2, kind: NotFound, message: "No such file or directory" }
}

fn open_file(path: &str) -> Result<File, io::Error>{
    let a = File::open(path)?;
    Ok(a)
}

上面的程式相當於:

use std::fs::File;
use std::io;

fn main() {
    let file = open_file("not-exist.txt");
    
    match file {
        Ok(file) => file,
        Err(err) => panic!("{:?}", err)
    };

    // Os { code: 2, kind: NotFound, message: "No such file or directory" }
}

fn open_file(path: &str) -> Result<File, io::Error>{
    let a = File::open(path);
    
    match a {
        Ok(file) => Ok(file),
        Err(err) => Err(err)
    }
}

寫在最後

錯誤處理是很大的一項課題,選擇處理的方式與時機也是學問,The Book 中有一篇 To panic! or Not to panic! 建議仔細閱讀。