Rust 將錯誤分為兩大類:可恢復的(recoverable)和 不可恢復的(unrecoverable)錯誤。對于一個可恢復的錯誤,比如文件未找到的錯誤,我們很可能只想向用戶報告問題并重試操作。不可恢復的錯誤總是 bug 出現的征兆,比如試圖訪問一個超過數組末端的位置,因此我們要立即停止程序。
Rust 的錯誤處理主要通過 Result 和 Option 類型來實現。Result 類型表示一個操作可能會成功(返回 Ok)或失敗(返回 Err),而 Option 類型表示一個值可能存在(Some)或不存在(None)。
一、不可恢復異常
C 語言中,嘗試讀取數據結構之后的值是未定義行為(undefined behavior)。你會得到任何對應數據結構中這個元素的內存位置的值,甚至是這些內存并不屬于這個數據結構的情況。這被稱為 緩沖區溢出(buffer overread),并可能會導致安全漏洞,比如攻擊者可以像這樣操作索引來讀取儲存在數據結構之后不被允許的數據。為了保護程序遠離這類漏洞,如果嘗試讀取一個索引不存在的元素,Rust 會停止執行并拒絕繼續。
fn main() {let v = vec![1, 2, 3];v[99];
}
運行產生異常
thread 'main' panicked at src\main.rs:246:6:
index out of bounds: the len is 2 but the index is 99
二、可恢復異常
使用result或者option進行異常處理。
fn divide(a: f64, b: f64) -> Result<f64, String> {if b == 0.0 {Err("除數不能為0".to_string())} else {Ok(a / b)}
}fn main() {let result = divide(10.0, 2.0);match result {Ok(value) => println!("結果: {}", value),Err(err) => println!("錯誤: {}", err),}
}
fn find_index(vec: &Vec<i32>, target: i32) -> Option<usize> {for (index, &value) in vec.iter().enumerate() {if value == target {return Some(index);}}None
}fn main() {let vec = vec![1, 2, 3, 4, 5];let index = find_index(&vec, 3);match index {Some(value) => println!("找到目標值,索引為: {}", value),None => println!("未找到目標值"),}
}
match已經可以判斷是否異常,但是比較長,還有是通過unwarp的方式,如果result的值是Ok,那么會返回結果,如果是Err,unwrap會調用panic!
use std::fs::File;
fn main() {let greeting_file = File::open("hello.txt").unwrap();
}
產生異常時
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os {
code: 2, kind: NotFound, message: "No such file or directory" }',
src/main.rs:4:49
也可以通過expect方法去自定義異常輸出。
三、錯誤的傳播
從一個方法里面拋出了錯誤,外層調用的方法怎么捕獲異常,類似java的throw try catch
fn read_username_from_file() -> Result<String, io::Error> {let username_file_result = File::open("hello.txt");let mut username_file = match username_file_result {Ok(file) => file,Err(e) => return Err(e),};let mut username = String::new();match username_file.read_to_string(&mut username) {Ok(_) => Ok(username),Err(e) => Err(e),}
}
也可以使用簡寫: ? 運算符
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {let mut username_file = File::open("hello.txt")?;let mut username = String::new();username_file.read_to_string(&mut username)?;Ok(username)
}
還可以進一步縮短
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {let mut username = String::new();File::open("hello.txt")?.read_to_string(&mut username)?;Ok(username)
}
哪里可以使用 ? 運算符
? 運算符只能被用于返回值與 ? 作用的值相兼容的函數。因為 ? 運算符被定義為從函數中提早返回一個值,這與上面寫的 match 表達式有著完全相同的工作方式。