0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

Rust中的錯誤處理方法

jf_wN0SrCdH ? 來源:Rust語言中文社區(qū) ? 2023-02-20 09:37 ? 次閱讀

Rust中的錯誤處理

Result枚舉

Rust 中沒有提供類似于 Java、C++ 中的 Exception 機制,而是使用Result枚舉的方式來實現(xiàn):


	

pub enum Result { /// Contains the success value Ok(T), /// Contains the error value Err(E), }

在使用時:

  • 如果無錯誤則使用Ok(T)返回;

  • 如果存在錯誤,則使用Err(E)包裝錯誤類型返回;

例如:

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(()) }

上面的代碼首先通過 MyError 枚舉定義了多個可能會出現(xiàn)的錯誤;

隨后,在add函數(shù)中:

  • 當 num 小于 0 時返回錯誤;

  • 否則給 num 增加 100000 并返回;

在上面的let res = add(1)?;中使用了?操作符,他相當于是一個語法糖:

  • 如果被調(diào)函數(shù)正常返回則調(diào)用unwrap獲取其值;

  • 反之,則將被調(diào)函數(shù)的錯誤直接向上返回(相當于直接 return Err);

即上面的語法糖相當于:


	

let res = match add() { Ok(id) => id, Err(err) => { return Err(err); } };

錯誤類型轉(zhuǎn)換

上面簡單展示了 Rust 中錯誤的使用;

由于 Rust 是強類型的語言,因此如果在一個函數(shù)中使用?返回了多個錯誤,并且他們的類型是不同的,還需要對返回的錯誤類型進行轉(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(()) }

上面的例子無法編譯通過,原因在于:read_to_stringparse返回的是不同類型的錯誤!

因此,如果要能返回,我們需要對每一個錯誤進行轉(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::()?; // Method 1: Handling error explicitly! let content = match std::read_to_string("/tmp/id") { Ok(content) => content, Err(err) => { return Err(MyError::ReadError(format!("read /tmp/id failed: {}", err))); } }; let content = content.trim(); println!("read content: {}", content); // Method 2: Use map_err to transform error type let id = content .parse::<i64>() .map_err(|err| MyError::ParseError(format!("parse error: {}", err)))?; Ok(id) }

上面展示了兩種不同的轉(zhuǎn)換 Error 的方法:

方法一通過 match 匹配手動的對read_to_string函數(shù)的返回值進行處理,如果發(fā)生了 Error,則將錯誤轉(zhuǎn)為我們指定類型的錯誤;

方法二通過map_err的方式,如果返回的是錯誤,則將其轉(zhuǎn)為我們指定的類型,這時就可以使用?返回了;

相比之下,使用 map_err 的方式,代碼會清爽很多!

From Trait

上面處理錯誤的方法,每次都要對錯誤的類型進行轉(zhuǎn)換,比較麻煩;

Rust 中提供了 From Trait,在進行類型匹配時,如果提供了從一個類型轉(zhuǎn)換為另一個類型的方法(實現(xiàn)了某個類型的 From Trait),則在編譯階段,編譯器會調(diào)用響應的函數(shù),直接將其轉(zhuǎn)為相應的類型!

例如:

examples/2_from_trait.rs


	

#[derive(Debug)] pub enum MyError { ReadError(String), ParseError(String), } impl From for MyError { fn from(source: std::Error) -> Self { MyError::ReadError(source.to_string()) } } impl From for MyError { fn from(source: std::ParseIntError) -> Self { MyError::ParseError(source.to_string()) } } fn read_file() -> Result<i64, MyError> { let _content = fs::read_to_string("/tmp/id")?; let content = _content.trim(); let id = content.parse::<i64>()?; Ok(id) } fn main() -> Result<(), MyError> { let id = read_file()?; println!("id: {}", id); Ok(()) }

在上面的代碼中,我們?yōu)?MyError 類型的錯誤分別實現(xiàn)了轉(zhuǎn)換為std::Errorstd::ParseIntError類型的 From Trait;

因此,在 read_file 函數(shù)中就可以直接使用?向上返回錯誤了!

但是上面的方法需要為每個錯誤實現(xiàn) From Trait 還是有些麻煩,因此出現(xiàn)了thiserror以及anyhow庫來解決這些問題;

其他第三方庫

thiserror

上面提到了我們可以為每個錯誤實現(xiàn) From Trait 來直接轉(zhuǎn)換錯誤類型,thiserror庫就是使用這個邏輯;

我們可以使用 thiserror 庫提供的宏來幫助我們生成到對應類型的 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(()) }

我們只需要對我們定義的類型進行宏標注,在編譯時這些宏會自動展開并實現(xiàn)對應的 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 for MyError { #[allow(deprecated)] fn from(source: std::Error) -> Self { MyError::IoError { 0: source } } } #[allow(unused_qualifications)] impl std::From for MyError { #[allow(deprecated)] fn from(source: std::ParseIntError) -> Self { MyError::ParseError { 0: source } } } #[automatically_derived] #[allow(unused_qualifications)] impl ::Debug for MyError { fn fmt(&self, f: &mut ::Formatter) -> ::Result { match (&*self,) { (&MyError::IoError(ref __self_0),) => { ::debug_tuple_field1_finish(f, "IoError", &&*__self_0) } (&MyError::ParseError(ref __self_0),) => { ::debug_tuple_field1_finish(f, "ParseError", &&*__self_0) } } } } fn read_file() -> Result<i64, MyError> { let content = fs::read_to_string("/tmp/id")?; let id = content.parse::<i64>()?; Ok(id) } #[allow(dead_code)] fn main() -> Result<(), MyError> { let id = read_file()?; { ::new_v1( &["id: ", " "], &[::new_display(&id)], )); }; Ok(()) } #[rustc_main] pub fn main() -> () { extern crate test; test::test_main_static(&[]) }

可以看到實際上就是為 MyError 實現(xiàn)了對應錯誤類型的 From Trait;

thiserror 庫的這種實現(xiàn)方式,還需要為類型指定要轉(zhuǎn)換的錯誤類型;

而下面看到的 anyhow 庫,可以將錯誤類型統(tǒng)一為同一種形式;

anyhow

如果你對 Go 中的錯誤類型不陌生,那么你就可以直接上手 anyhow 了!

來看下面的例子:

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,而非標準庫中的 Result 類型!

anyhowResult實現(xiàn)了ContextTrait:


	

impl Context for Result where E: ext::StdError + Send + Sync + 'static, { fn context(self, context: C) -> Result where C: Display + Send + Sync + 'static, { // Not using map_err to save 2 useless frames off the captured backtrace // in ext_context. match self { Ok(ok) => Ok(ok), Err(error) => Err(error.ext_context(context)), } } fn with_context(self, context: F) -> Result where C: Display + Send + Sync + 'static, F: FnOnce() -> C, { match self { Ok(ok) => Ok(ok), Err(error) => Err(error.ext_context(context())), } } }

Context中提供了context函數(shù),并且將原來的Result轉(zhuǎn)成了Result;

因此,最終將錯誤類型統(tǒng)一為了anyhow::Error類型;

附錄

源代碼:

  • https://github.com/JasonkayZK/rust-learn/tree/error

審核編輯:湯梓紅


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2946

    瀏覽量

    104372
  • 函數(shù)
    +關注

    關注

    3

    文章

    4260

    瀏覽量

    62232
  • C++
    C++
    +關注

    關注

    21

    文章

    2090

    瀏覽量

    73405
  • 枚舉
    +關注

    關注

    0

    文章

    16

    瀏覽量

    4564
  • Rust
    +關注

    關注

    1

    文章

    228

    瀏覽量

    6526

原文標題:[Rust筆記] Rust中的錯誤處理

文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關推薦

    嵌入式編程錯誤處理機制設計

    本文主要總結嵌入式系統(tǒng)C語言編程,主要的錯誤處理方式。文中涉及的代碼運行環(huán)境如下。
    發(fā)表于 04-28 09:59 ?720次閱讀
    嵌入式編程<b class='flag-5'>錯誤處理</b>機制設計

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

    本文主要總結嵌入式系統(tǒng)C語言編程,主要的錯誤處理方式。
    發(fā)表于 07-24 16:40 ?836次閱讀
    嵌入式系統(tǒng)C語言編程<b class='flag-5'>中</b>主要的<b class='flag-5'>錯誤處理</b>方式

    Rust語言中錯誤處理的機制

    可能的錯誤,實際運行仍然可能出現(xiàn)各種各樣的錯誤,比如文件不存在、網(wǎng)絡連接失敗等等。對于這些不可預測的錯誤,我們必須使用錯誤處理機制來進行
    的頭像 發(fā)表于 09-19 14:54 ?1290次閱讀

    嵌入式C編程常用的異常錯誤處理

    嵌入式C編程,異常錯誤處理是確保系統(tǒng)穩(wěn)定性和可靠性的重要部分。以下是一些常見的異常錯誤處理方法及其詳細說明和示例: 1. 斷言 (Assertions) 斷言用于在開發(fā)階段捕獲程
    發(fā)表于 08-06 14:32

    labviEW錯誤處理的問題

    為什么這個程序在啟用自動錯誤處理和C:\data.txt不存在的情況下,沒有顯示錯誤對話框???
    發(fā)表于 04-01 10:03

    LabVIEW錯誤處理問題

    我想問一下,就是連接硬件采集波形時,需要濾掉直流波,但是采集到的波形時斷斷續(xù)續(xù)的,所以錯誤處理時會停止程序,我想問一下,運行時怎么忽略掉這個錯誤
    發(fā)表于 09-18 18:29

    AF錯誤處理

    想問一下關于AF的錯誤處理,例如我進行串口通訊,打開串口錯誤,但是我不想停止AF,想繼續(xù)嘗試連接要怎么做?
    發(fā)表于 02-03 15:44

    LabVIEW錯誤處理

    如何合理使用 LabVIEW 的自定義錯誤處理功能;對于可預見的錯誤,是否可以選擇直 接忽略,或者前幾次嘗試忽略直到該特定錯誤出現(xiàn)很多次后才通知用戶需要糾正該
    發(fā)表于 05-24 11:07 ?6次下載

    Spring Boot框架錯誤處理

    》 《strong》翻譯《/strong》:雁驚寒《/p》 《/blockquote》《p》《em》摘要:本文通過實例介紹了使用Spring Boot在設計API的時候如何正確地對異常進行處理。以下是譯文《/em》《/p》《p》API在提供錯誤消息的同時進行適當?shù)?/div>
    發(fā)表于 09-28 15:31 ?0次下載

    嵌入式系統(tǒng)C語言編程錯誤處理資料總結

    本文主要總結嵌入式系統(tǒng)C語言編程,主要的錯誤處理方式。文中涉及的代碼運行環(huán)境如下:
    發(fā)表于 11-28 10:39 ?1878次閱讀

    Rust代碼啟發(fā)之返回值異常錯誤處理

    這樣的代碼,錯誤處理代碼和業(yè)務邏輯交織在一起,也容易忽略處理錯誤。以及把返回值只用于錯誤返回,有點浪費的感覺。因為很多時候把計算結果作為返回值,更符合思考的邏輯。
    的頭像 發(fā)表于 09-22 09:24 ?2051次閱讀
    <b class='flag-5'>Rust</b>代碼啟發(fā)之返回值異常<b class='flag-5'>錯誤處理</b>

    rust語言基礎學習: rust錯誤處理

    錯誤是軟件不可避免的,所以 Rust 有一些處理出錯情況的特性。在許多情況下,Rust 要求你承認錯誤
    的頭像 發(fā)表于 05-22 16:28 ?1954次閱讀

    RS232通信時怎么處理錯誤?RS232通信中的錯誤處理方法

    RS232通信時怎么處理錯誤?RS232通信中的錯誤處理方法? RS232通信是一種電氣標準,它定義了計算機和串行通信設備之間的通信協(xié)議。盡管RS232通信很穩(wěn)定,但仍然可能會出現(xiàn)
    的頭像 發(fā)表于 10-17 16:33 ?2636次閱讀

    西門子博圖:錯誤處理機制概覽

    可通過以下幾種不同的錯誤處理機制進行參數(shù)跟蹤或編程或訪問錯誤
    的頭像 發(fā)表于 11-25 11:35 ?2449次閱讀
    西門子博圖:<b class='flag-5'>錯誤處理</b>機制概覽

    C語言中的錯誤處理機制解析

    C 語言不提供對錯誤處理的直接支持,但是作為一種系統(tǒng)編程語言,它以返回值的形式允許您訪問底層數(shù)據(jù)。
    的頭像 發(fā)表于 02-26 11:19 ?436次閱讀