# Error Handling > 異常在多數語言中會統一以 `Exception` 來處理, 而 `Java` 提供了 `Exception` 和 `Error` 來進一步區分是遇到比較輕微的「例外」或比較嚴重的「錯誤」, 並把 `Exception` 區分為執行時期 (i.e. 使用者自己不當使用) 或非執行時期 (內部錯誤) 的例外。 > > Rust 也採用將異常依程度分成兩種, 並更進一步的規定處理這兩種例外的方法。 這兩種異常的使用有一些指導性原則, 當遇到... 1. 測試 2. 程式原型的標記 3. 一定不會錯 4. 別人調用你的函式 5. 自己設定的不變量或規則被破壞 此時使用 `panic!` 或會調用 `panic!` 的 `unwrap` 或 `expect`。 其他情況則建議用 `Result`。 ## 例外分類 ### `Panic`: Unrecoverable Errors 若遇到嚴重而不可回復的錯誤, 若我們自己撰寫時偵測到的話, 可以調用 `panic!("[opt]Error Msg")` 來主動引發 panic, 或者調用其它庫時使用不慎, 其內部引發 panic。 `panic!()` 會... 1. 印出錯誤訊息 2. `unwind` (展開) `stack` (呼叫函式的 stack) 3. 退出程式 這裡的 `unwind stack` Rust 會一步步回朔並清理每個區塊的記憶體, 工作量大, 可以改成 `abort`, 清理資料的工作便會由作業系統來處理, 這樣的一個明顯好處是, 如此產生的執行檔會更輕巧。 而改成 `abort` 的方法是在 `Cargo.toml` 中添加 ```txt [profile.release] panic = 'abort' ``` ### `Result`: Recoverable Errors `Result` 是一個 `enum`, 會自動預先導入 (prelude), 同 `Option`。 ```Rust= enum Result<T, E> { Ok(T), Err(E) } ``` 來個處理回傳值為 `Result<T, E>` 的例子。 ```Rust= /* use std::fs::File; */ /* use std::io::ErrorKind; */ let file_name = "Eroiko.txt"; /* 土法煉鋼法 */ let old_fashion_f = File::open(file_name); let old_fashion_f = match old_fashion_f { Ok(file) => file, Err(e) => match e.kind() { ErrorKind::NotFound => match File::create(file_name) { Ok(_file) => _file, Err(e) => panic!("Error occur when creating file: {}", e), } otherwise => panic!("Error occur when opening file: {:?}", otherwise), } }; /* 用 Closure 來處理 */ let nice_f = File::open(file_name).unwrap_or_else( |e| { if e.kind() == ErrorKind::NotFound { File::create(file_name).unwrap_or_else(|e| { panic!("Error occur when creating file: {}", e); }) } else { panic!("Error occur when opening file: {:?}", e); } }); /* 懶得處理例外 */ // + 1. 直接解開 Result<T, E>, 遇到錯誤時無自定義錯誤訊息 let unwrap_f = File::open(file_name).unwrap(); // * 以上等價於 let real_unwrap_f = File::open(file_name); let real_unwrap_f = match real_unwrap_f { Ok(file) => file, Err(e) => { panic!("Error occur when opening file: {:?}", e) } }; // + 2. 解開 Result 時遇到錯誤,會印出自定義錯誤訊息的版本 let lazy_f = File::open(file_name).expect("Error occur when opening file"); ``` ## Error Propagating > 錯誤傳遞 透過將錯誤包裝於 `Err` 後 `return` 來實現,這種函式會返回一個 `Result<T, E>`。 舉例如下。 ```Rust= use std::fs::File; use std::io::{self, Read}; fn file_read(file_name: &str) -> Result<String, io::Error> { let f = File::open(file_name); let mut f = match f { // f is mutable since f.read_to_string is mutative Ok(fl) => fl, Err(e) => return Err(e), }; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } } ``` 不過上面其實很冗餘,可以用 `?` 運算子來簡化處理 `match` 的調用結果。 ```Rust= fn file_read_with_qm(file_name: &str) -> Result<String, io::Error> { let mut s = String::new(); File::open(file_name)?.read_to_string(&mut s)?; Ok(s) } ``` `?` 的作用是,若對一個為 `Result::Ok` 的表達式, 會將其拆包; 若為 `Result::Err`, 則會<font color = mediumSeaGreen>將此 `Err` 隱式的<font color=#cd4c4c size=4>以 `trait std::convert::From` 中定義的 `from` 函式轉換成對應返回之 `Err` 型別 </font> 後返回</font>, 這等價於上面的寫法。 所以這意味著 `Err<A>` 必須實作 `trait std::convert::From<A, B>`, 使原 `Err<A>` 可以轉換成的 `Err<B>`。 另外, 理所當然的, 只有當一個函式的返回值為 `Result` 時, 才可以在其 scope 內使用 `?` 運算子。 > 另外提一下, `Box<dyn Error>` 表示所有 `Error`