Rust中的Result列舉
阿新 • • 發佈:2021-11-28
Result列舉在Rust中是使用頻率極高的一個型別,常用於函式的返回值定義,其原始碼如下:
#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] #[must_use = "this `Result` may be an `Err` variant, which should be handled"] #[rustc_diagnostic_item = "result_type"] #[stable(feature = "rust1", since = "1.0.0")] pub enum Result<T, E> { /// Contains the success value #[lang = "Ok"] #[stable(feature = "rust1", since = "1.0.0")] Ok(#[stable(feature = "rust1", since = "1.0.0")] T), /// Contains the error value #[lang = "Err"] #[stable(feature = "rust1", since = "1.0.0")] Err(#[stable(feature = "rust1", since = "1.0.0")] E), }
拋開一堆#開頭的trait不看,核心就是Ok及Err這2個泛型成員。
先來看一個基本示例:
let result_ok: Result<String, String> = Result::Ok(String::from("success")); let result = match result_ok { Result::Ok(o) => o, Result::Err(e) => e, }; println!("{}", result);
這裡定義了一個"成功"的Result,然後使用模式匹配對其進行處理,如果是Ok的,取出Ok的值,否則取出Err的值。這類簡單重複的判斷,經常會用到,rust封裝了1個等效的方法:unwrap,其內部實現如下:
pub fn unwrap(self) -> T { match self { Ok(t) => t, Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e), } }
其實就是模式匹配,取出Ok或Err的值。所以前面這個示例,可以改寫成:
let result_ok: Result<String, String> = Result::Ok(String::from("success")); // let result = match result_ok { // Result::Ok(o) => o, // Result::Err(e) => e, // }; let result = result_ok.unwrap(); println!("{}", result);
unwrap原始碼中的unwrap_failed繼續追下去的話,可以看到:
fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! { panic!("{}: {:?}", msg, error) }
呼叫了panic方法,這意味著如果Result返回的是Err,則程式會崩潰,可以試一把:
如果Err發生時不希望程式崩潰,可以使用unwrap_or()
let result_fail: Result<String, String> = Result::Err(String::from("failure")); let result = result_fail.unwrap_or(String::from("err occur")); println!("{}", result);
unwrap_or可以傳入一個default預設的錯誤值,上面這段將輸出“err occur”。但這樣一來,就把原始的錯誤資訊failure給丟失了! 不用擔心,rust早考慮到了:
let result_fail: Result<String, String> = Result::Err(String::from("failure")); let result = result_fail.unwrap_or_else(|e|e); println!("{}", result);
使用unwrap_or_else傳入1個閉包匿名函式,可以隨心所欲的對原始錯誤進行處理,這裡我們啥也沒幹,|e|e,表示原樣返回。
Result列舉還提供了其它一些常用方法,參見上圖,有興趣的同學,可以研究下原始碼。
最後來看一個稍微複雜點的示例:在當前目錄下,開啟hello.txt檔案,如果該檔案不存在,則自動建立一個空的hello.txt。
use core::panic; use std::fs::File; use std::io::{ErrorKind}; fn main() { let file_name = String::from("hello.txt"); //第1層match match File::open(&file_name) { Ok(file) => file, //第2層match Err(error) => match error.kind() { //第3層match ErrorKind::NotFound => match File::create(&file_name) { Ok(fc) => fc, Err(e) => panic!("Error creating file:{:?}", e), }, oe => panic!("Error opening the file:{:?}", oe), }, }; }
用了3層模式匹配(match套娃),看上去比較原始,如果不喜歡這種match寫法,可以用今天學到的知識,換成相對“正常點”的寫法:
File::open(&file_name).unwrap_or_else(|e| { if e.kind() == ErrorKind::NotFound { File::create(&file_name).unwrap_or_else(|e| { panic!("Error creating file:{:?}", e); }) } else { panic!("Error opening file:{:?}", e) } });
Rust程式設計師,可能會寫得更簡短:
File::open(&file_name).unwrap_or_else(|e| match e.kind() { ErrorKind::NotFound => { File::create(&file_name).unwrap_or_else(|e| panic!("Error creating file:{:?}", e)) } _ => panic!("Error opening file:{:?}", e) });作者:菩提樹下的楊過
出處:http://yjmyzz.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。