最近在學習總結Rust的各種場景的語法模式,也就是Rust寫代碼的模式。
今天分享關于Rust的錯誤處理的三大類語法模式。
第一類:有意不處理錯誤,忽略錯誤
第二類:對錯誤做自定義信息提示
第三類:推薦!根據正確和錯誤情況分開處理,錯誤還可以進一步分流處理
Rust的錯誤處理方式比起Golang更靈活,可以針對錯誤和當下代碼需要賦值前做錯誤判斷、故意忽略異常、異常時打印錯誤并終止等的不同編碼場景,選用不同的語法模式。
錯誤處理涉及到數據類型、錯誤處理的控制語法、相關的crate模塊。
Result屬于Rust的核心crate提供的功能(core::result::Result)。Result 有2個特點:
首先,Result<T,E> 屬于泛型,所以T和E可以是任意類型。但常見的使用模式是以T存儲正常情況的信息,E存儲錯誤和異常情況的信息。
其次,Result是枚舉類型,其內部實際只包含:
Ok()Err()
這兩個枚舉,所以它的實例只會是Ok()或Err()之一。
Result的代碼實現中就用的枚舉類型的,代碼如下:
/// Result的定義在 rust核心代碼 src/rust/library/core/src/result.rs 代碼文件中:/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).////// See the [module documentation](self "module documentation") for details.#[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"]#[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),}
實際使用的一個例子:例如Result<String, &str> 是一個泛型類型,則此時的T類型為String,E類型為&str。它表示一個可能成功或失敗的操作的結果,其中成功時的返回類型為 String,失敗時的錯誤類型為 &str。
總結一下Result<Ok(),Err()>: result 類型通常用于表示可能成功或失敗的操作的結果。它使用 Result<T, E> 類型來表示,其中 T 是成功時的返回類型,E 是失敗時的錯誤類型。
Option也是枚舉類型,其內部實際只包含兩種值:Some()和None,這個可以從其代碼中得到印證。Option的實現代碼為:
/// 代碼文件 rustlib/src/rust/library/core/src/option.rs 定義了Option/// The `Option` type. See [the module level documentation](self "the module level documentation") for more.#[derive(Copy, PartialOrd, Eq, Ord, Debug, Hash)]#[rustc_diagnostic_item = "Option"]#[lang = "Option"]#[stable(feature = "rust1", since = "1.0.0")]pub enum Option<T> { /// No value. #[lang = "None"] #[stable(feature = "rust1", since = "1.0.0")] None, /// Some value of type `T`. #[lang = "Some"] #[stable(feature = "rust1", since = "1.0.0")] Some(#[stable(feature = "rust1", since = "1.0.0")] T),}
首先,從名稱上理解一下:
其次,它們之間可以互相轉化。對于錯誤處理來說,match處理Result或Option時,都要處理錯誤或None等異常值,就實現了Rust的最重要的錯誤處理邏輯。
(1) unwrap()
在 Rust 中,unwrap() 方法用于從 Result 類型中提取成功時的返回值。如果 Result 類型的值是 Ok(表示成功),則 unwrap() 方法將返回 T;如果 Result 類型的值是 Err(表示失敗),則 unwrap() 方法將觸發一個 panic,拋出一個 E 類型的錯誤。如果您在調用 unwrap() 方法時遇到錯誤,說明您正在處理一個 Err 類型的值,即失敗的情況。在這種情況下,您應該使用其他方法來處理錯誤,而不是直接使用 unwrap() 方法。總之,在處理 Result 類型時,應該始終考慮可能的失敗情況,并使用適當的方法來處理錯誤。直接使用 unwrap() 方法可能會導致程序崩潰(panic),因此應該謹慎使用。只應該在非生產的代碼中使用。
(2) .fn()?符號
這個符號在Rust中的術語是“提前返回選項”(early return option),作用等同于unwrap()。只允許用于返回Result<>或Option<>類型的函數之后。在rust的早期版本中,有個try!宏具有等效的功能。
代碼演示:(演示打開文件,返回Result<File, Error> 類型的值,然后可以被main()中的match方式處理):
use std::fs::File;use std::io::Error;fn open_file(file_path: &str) -> Result<File, Error> { let mut file = File::open(file_path)?; Ok(file) }
這段函數內部使用File::open(file_path)?; 打開指定路徑的文件,open()是rust的內部函數,原始定義為:
pub fn open<P>(path: P) -> io::Result<File>
而演示代碼中有意忽略了錯誤的情況,以?結尾,來調用open()函數:
let mut file = File::open(file_path)?;
expect() 可作為代替unwrap()或 ? 相比unwrap(), expect()是一個更好的選擇,因為它允許發生錯誤時打印一個簡單的消息并終止運行。
根據正確和錯誤情況分開處理,錯誤還可以進一步分流處理。
(1) match,根據正確和錯誤情況分開處理。
use std::fs::File;use std::io::Error;/// 演示打開文件,返回Result<File, Error> 類型的值,然后被main()中的match方式處理fn open_file(file_path: &str) -> Result<File, Error> { let mut file = File::open(file_path)?; Ok(file)}fn main() { let file_path = "file.txt"; let file = open_file(file_path); match file { Ok(file) => println!("文件打開成功 {:?}", file), Err(error) => println!("文件打開失敗 {}", error), }}
use std::fs::File;use std::io::ErrorKind;use std::io::{Error, Read};/// 演示文件打開時 如何返回Option<T> 類型值fn open_file(file_path: &str) -> Option<File> { let mut file = File::open(file_path).unwrap(); Some(file)}/// 演示 match 如何處理 Option<T> 類型值,其中有None類型的情況fn read_file(file: Option<File>) -> Result<String, Error> { match file { //處理文件的正常情況 Some(mut file) => { let mut buffer = String::new(); let file_content = file.read_to_string(&mut buffer); Ok(buffer) } //處理文件的異常情況 None => Err(Error::new(ErrorKind::NotFound, "File not found")), }}fn main() { let file_path = "file.txt"; let file = open_file(&file_path); let strings_in_file = read_file(file); /// 在文件 file.txt 不存在的情況下,以下代碼會導致軟件崩潰。 println!("{}", strings_in_file.unwrap()); ///echo "1111">> file.txt /// 創建 file.txt文件 /// 然后重復上面代碼}
map_err() 將在以后發布的文章中再講解。本文不做詳細介紹。
(2) if let,適合直接在賦值前做錯誤處理。代碼模式為:
let final_value = if let Some(T) = Rust語句 { //語句正確和成功的情況,如獲取有效數據,將作為作用域的返回值賦值給final_value } else { //錯誤或異常的情況的處理,如賦值為"",同樣會作為作用域的返回值賦值給 final_value }
以下是一些常見的處理錯誤的方法:使用 match 表達式:通過使用 match 表達式,您可以根據 Result 類型的值來執行不同的操作。如果是 Ok 類型,可以提取成功的值;如果是 Err 類型,可以處理錯誤。使用 map_err() 方法:map_err() 方法可以將 Err 類型的值轉換為另一種錯誤類型,并返回一個新的 Result 類型。使用 and_then() 或 or_else() 方法:這些方法可以在成功或失敗的情況下執行不同的操作,并返回一個新的 Result 類型。
(3) 使用特定的函數:and_then() 和 or_else()和 ok_or()
這3個函數在Rust中的術語為組合算子,如果你已理解C/C++中的 && 和 ||或 Python中的and以及or語法的意義,那么你大概已經理解了 and_then() 這3個函數的意思。比如 and_then()是當調用者為true或調用者為正常的時候,才會調用and_then(...)函數。那么對于錯誤處理就非常有用。
下面的代碼例子演示了烹飪的邏輯:當有食材的時候,才能按照食譜制作好菜品。隱含的意思就是(錯誤的情況下),沒有食材的情況下,就不用照著食譜做菜了。
#![allow(dead_code)]#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }// 我們沒有原材料(ingredient)來制作壽司。fn have_ingredients(food: Food) -> Option<Food> { match food { Food::Sushi => None, _ => Some(food), }}// 我們擁有全部食物的食譜,除了欠缺高超的烹飪手藝。fn have_recipe(food: Food) -> Option<Food> { match food { Food::CordonBleu => None, _ => Some(food), }}// 做一份好菜,我們需要原材料和食譜這兩者。// 我們可以借助一系列 `match` 來表達相應的邏輯:// (原文:We can represent the logic with a chain of `match`es:)fn cookable_v1(food: Food) -> Option<Food> { match have_ingredients(food) { None => None, Some(food) => match have_recipe(food) { None => None, Some(food) => Some(food), }, }}// 這可以使用 `and_then()` 方便重寫出更緊湊的代碼:fn cookable_v2(food: Food) -> Option<Food> { have_ingredients(food).and_then(have_recipe)}fn eat(food: Food, day: Day) { match cookable_v2(food) { Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food), None => println!("Oh no. We don't get to eat on {:?}?", day), }}fn main() { let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi); eat(cordon_bleu, Day::Monday); eat(steak, Day::Tuesday); eat(sushi, Day::Wednesday);}
本文鏈接:http://www.www897cc.com/showinfo-26-50775-0.html入門Rust的固定套路:錯誤處理模式有三大類,幫你總結了
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 系統設計小抄 - 如何做到高可用、高吞吐、高擴展性
下一篇: 用了這個庫,真的可以丟掉任務管理器了