Rust中的錯(cuò)誤處理
Result枚舉
Rust 中沒有提供類似于 Java、C++ 中的 Exception 機(jī)制,而是使用Result
枚舉的方式來(lái)實(shí)現(xiàn):
pub enum Result
在使用時(shí):
-
如果無(wú)錯(cuò)誤則使用
Ok(T)
返回; -
如果存在錯(cuò)誤,則使用
Err(E)
包裝錯(cuò)誤類型返回;
例如:
examples/0_result.rs
#[derive(Debug)] pub enum MyError { Internal(String), InvalidId(String), } fn add(num: i64) -> Result<i64, MyError> { if num < 0 { Err(MyError::InvalidId(String::from("Invalid num!"))) } else { Ok(num + 100000) } } fn main() -> Result<(), MyError> { // fetch_id(-1)?; let res = add(1)?; println!("{}", res); Ok(()) }
上面的代碼首先通過(guò) MyError 枚舉定義了多個(gè)可能會(huì)出現(xiàn)的錯(cuò)誤;
隨后,在add
函數(shù)中:
-
當(dāng) num 小于 0 時(shí)返回錯(cuò)誤;
-
否則給 num 增加 100000 并返回;
在上面的let res = add(1)?;
中使用了?
操作符,他相當(dāng)于是一個(gè)語(yǔ)法糖:
-
如果被調(diào)函數(shù)正常返回則調(diào)用
unwrap
獲取其值; -
反之,則將被調(diào)函數(shù)的錯(cuò)誤直接向上返回(相當(dāng)于直接 return Err);
即上面的語(yǔ)法糖相當(dāng)于:
let res = match add() { Ok(id) => id, Err(err) => { return Err(err); } };
錯(cuò)誤類型轉(zhuǎn)換
上面簡(jiǎn)單展示了 Rust 中錯(cuò)誤的使用;
由于 Rust 是強(qiáng)類型的語(yǔ)言,因此如果在一個(gè)函數(shù)中使用?
返回了多個(gè)錯(cuò)誤,并且他們的類型是不同的,還需要對(duì)返回的錯(cuò)誤類型進(jìn)行轉(zhuǎn)換,轉(zhuǎn)為相同的類型!
例如下面的例子:
#[derive(Debug)] pub enum MyError { ReadError(String), ParseError(String), } fn read_file() -> Result<i64, MyError> { // Error: Could not get compiled! let content = fs::read_to_string("/tmp/id")?; let id = content.parse::<i64>()?; } fn main() -> Result<(), MyError> { let id = read_file()?; println!("id: {}", id); Ok(()) }
上面的例子無(wú)法編譯通過(guò),原因在于:read_to_string
和parse
返回的是不同類型的錯(cuò)誤!
因此,如果要能返回,我們需要對(duì)每一個(gè)錯(cuò)誤進(jìn)行轉(zhuǎn)換,轉(zhuǎn)為我們所定義的 Error 類型;
例如:
examples/1_error_convert.rs
fn read_file() -> Result<i64, MyError> { // Error: Could not get compiled! // let content = fs::read_to_string("/tmp/id")?; // let id = content.parse::
上面展示了兩種不同的轉(zhuǎn)換 Error 的方法:
方法一通過(guò) match 匹配手動(dòng)的對(duì)read_to_string
函數(shù)的返回值進(jìn)行處理,如果發(fā)生了 Error,則將錯(cuò)誤轉(zhuǎn)為我們指定類型的錯(cuò)誤;
方法二通過(guò)map_err
的方式,如果返回的是錯(cuò)誤,則將其轉(zhuǎn)為我們指定的類型,這時(shí)就可以使用?
返回了;
相比之下,使用 map_err 的方式,代碼會(huì)清爽很多!
From Trait
上面處理錯(cuò)誤的方法,每次都要對(duì)錯(cuò)誤的類型進(jìn)行轉(zhuǎn)換,比較麻煩;
Rust 中提供了 From Trait,在進(jìn)行類型匹配時(shí),如果提供了從一個(gè)類型轉(zhuǎn)換為另一個(gè)類型的方法(實(shí)現(xiàn)了某個(gè)類型的 From Trait),則在編譯階段,編譯器會(huì)調(diào)用響應(yīng)的函數(shù),直接將其轉(zhuǎn)為相應(yīng)的類型!
例如:
examples/2_from_trait.rs
#[derive(Debug)] pub enum MyError { ReadError(String), ParseError(String), } impl From
在上面的代碼中,我們?yōu)?MyError 類型的錯(cuò)誤分別實(shí)現(xiàn)了轉(zhuǎn)換為std::Error
和std::ParseIntError
類型的 From Trait;
因此,在 read_file 函數(shù)中就可以直接使用?
向上返回錯(cuò)誤了!
但是上面的方法需要為每個(gè)錯(cuò)誤實(shí)現(xiàn) From Trait 還是有些麻煩,因此出現(xiàn)了thiserror以及anyhow庫(kù)來(lái)解決這些問(wèn)題;
其他第三方庫(kù)
thiserror
上面提到了我們可以為每個(gè)錯(cuò)誤實(shí)現(xiàn) From Trait 來(lái)直接轉(zhuǎn)換錯(cuò)誤類型,thiserror
庫(kù)就是使用這個(gè)邏輯;
我們可以使用 thiserror 庫(kù)提供的宏來(lái)幫助我們生成到對(duì)應(yīng)類型的 Trait;
例如:
examples/3_thiserror.rs
#[derive(thiserror::Error, Debug)] pub enum MyError { #[error("io error.")] IoError(#[from] std::Error), #[error("parse error.")] ParseError(#[from] std::ParseIntError), } fn read_file() -> Result<i64, MyError> { // Could get compiled! let content = fs::read_to_string("/tmp/id")?; let id = content.parse::<i64>()?; Ok(id) } fn main() -> Result<(), MyError> { let id = read_file()?; println!("id: {}", id); Ok(()) }
我們只需要對(duì)我們定義的類型進(jìn)行宏標(biāo)注,在編譯時(shí)這些宏會(huì)自動(dòng)展開并實(shí)現(xiàn)對(duì)應(yīng)的 Trait;
展開后的代碼如下:
#![feature(prelude_import)] #[prelude_import] use std::*; #[macro_use] extern crate std; use std::fs; pub enum MyError { #[error("io error.")] IoError(#[from] std::Error), #[error("parse error.")] ParseError(#[from] std::ParseIntError), } #[allow(unused_qualifications)] impl std::Error for MyError { fn source(&self) -> std::Option<&(dyn std::Error + 'static)> { use thiserror::AsDynError; #[allow(deprecated)] match self { MyError::IoError { 0: source, .. } => std::Option::Some(source.as_dyn_error()), MyError::ParseError { 0: source, .. } => { std::Option::Some(source.as_dyn_error()) } } } } #[allow(unused_qualifications)] impl std::Display for MyError { fn fmt(&self, __formatter: &mut std::Formatter) -> std::Result { #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] match self { MyError::IoError(_0) => { let result = __formatter.write_fmt(::new_v1(&["io error."], &[])); result } MyError::ParseError(_0) => { let result = __formatter.write_fmt(::new_v1(&["parse error."], &[])); result } } } } #[allow(unused_qualifications)] impl std::From
可以看到實(shí)際上就是為 MyError 實(shí)現(xiàn)了對(duì)應(yīng)錯(cuò)誤類型的 From Trait;
thiserror 庫(kù)的這種實(shí)現(xiàn)方式,還需要為類型指定要轉(zhuǎn)換的錯(cuò)誤類型;
而下面看到的 anyhow 庫(kù),可以將錯(cuò)誤類型統(tǒng)一為同一種形式;
anyhow
如果你對(duì) Go 中的錯(cuò)誤類型不陌生,那么你就可以直接上手 anyhow 了!
來(lái)看下面的例子:
examples/4_anyhow.rs
use anyhow::Result; use std::fs; fn read_file() -> Result<i64> { // Could get compiled! let content = fs::read_to_string("/tmp/id")?; let id = content.parse::<i64>()?; Ok(id) } fn main() -> Result<()> { let id = read_file()?; println!("id: {}", id); Ok(()) }
注意到,上面的 Result 類型為anyhow::Result
,而非標(biāo)準(zhǔn)庫(kù)中的 Result 類型!
anyhow
為Result
實(shí)現(xiàn)了Context
Trait:
impl
在Context
中提供了context
函數(shù),并且將原來(lái)的Result
轉(zhuǎn)成了Result
;
因此,最終將錯(cuò)誤類型統(tǒng)一為了anyhow::Error
類型;
附錄
源代碼:
-
https://github.com/JasonkayZK/rust-learn/tree/error
審核編輯:湯梓紅
-
JAVA
+關(guān)注
關(guān)注
20文章
2984瀏覽量
106932 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4371瀏覽量
64223 -
C++
+關(guān)注
關(guān)注
22文章
2117瀏覽量
74789 -
枚舉
+關(guān)注
關(guān)注
0文章
16瀏覽量
4690 -
Rust
+關(guān)注
關(guān)注
1文章
233瀏覽量
6965
原文標(biāo)題:[Rust筆記] Rust中的錯(cuò)誤處理
文章出處:【微信號(hào):Rust語(yǔ)言中文社區(qū),微信公眾號(hào):Rust語(yǔ)言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
嵌入式編程錯(cuò)誤處理機(jī)制設(shè)計(jì)

嵌入式系統(tǒng)C語(yǔ)言編程中主要的錯(cuò)誤處理方式

Rust語(yǔ)言中錯(cuò)誤處理的機(jī)制
嵌入式C編程常用的異常錯(cuò)誤處理
labviEW錯(cuò)誤處理的問(wèn)題
AF錯(cuò)誤處理
LabVIEW中的錯(cuò)誤處理
Spring Boot框架錯(cuò)誤處理
嵌入式系統(tǒng)C語(yǔ)言編程中的錯(cuò)誤處理資料總結(jié)
Rust代碼啟發(fā)之返回值異常錯(cuò)誤處理

評(píng)論