0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Rust中的错误处理方法

jf_wN0SrCdH 来源:Rust语言中文社区 2023-02-20 09:37 次阅读

Rust中的错误处理

Result枚举

Rust 中没有提供类似于 JavaC++ 中的 Exception 机制,而是使用Result枚举的方式来实现:


	

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 枚举定义了多个可能会出现的错误;

随后,在add函数中:

  • 当 num 小于 0 时返回错误;

  • 否则给 num 增加 100000 并返回;

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

  • 如果被调函数正常返回则调用unwrap获取其值;

  • 反之,则将被调函数的错误直接向上返回(相当于直接 return Err);

即上面的语法糖相当于:


	

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

错误类型转换

上面简单展示了 Rust 中错误的使用;

由于 Rust 是强类型的语言,因此如果在一个函数中使用?返回了多个错误,并且他们的类型是不同的,还需要对返回的错误类型进行转换,转为相同的类型!

例如下面的例子:


	

#[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返回的是不同类型的错误!

因此,如果要能返回,我们需要对每一个错误进行转换,转为我们所定义的 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) }

上面展示了两种不同的转换 Error 的方法:

方法一通过 match 匹配手动的对read_to_string函数的返回值进行处理,如果发生了 Error,则将错误转为我们指定类型的错误;

方法二通过map_err的方式,如果返回的是错误,则将其转为我们指定的类型,这时就可以使用?返回了;

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

From Trait

上面处理错误的方法,每次都要对错误的类型进行转换,比较麻烦;

Rust 中提供了 From Trait,在进行类型匹配时,如果提供了从一个类型转换为另一个类型的方法(实现了某个类型的 From Trait),则在编译阶段,编译器会调用响应的函数,直接将其转为相应的类型!

例如:

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

在上面的代码中,我们为 MyError 类型的错误分别实现了转换为std::Errorstd::ParseIntError类型的 From Trait;

因此,在 read_file 函数中就可以直接使用?向上返回错误了!

但是上面的方法需要为每个错误实现 From Trait 还是有些麻烦,因此出现了thiserror以及anyhow库来解决这些问题;

其他第三方库

thiserror

上面提到了我们可以为每个错误实现 From Trait 来直接转换错误类型,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(()) }

我们只需要对我们定义的类型进行宏标注,在编译时这些宏会自动展开并实现对应的 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 实现了对应错误类型的 From Trait;

thiserror 库的这种实现方式,还需要为类型指定要转换的错误类型;

而下面看到的 anyhow 库,可以将错误类型统一为同一种形式;

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实现了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函数,并且将原来的Result转成了Result

因此,最终将错误类型统一为了anyhow::Error类型;

附录

源代码:

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

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2904

    浏览量

    103000
  • 函数
    +关注

    关注

    3

    文章

    3904

    浏览量

    61310
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72901
  • 枚举
    +关注

    关注

    0

    文章

    16

    浏览量

    4524
  • Rust
    +关注

    关注

    1

    文章

    223

    浏览量

    6387

原文标题:[Rust笔记] Rust中的错误处理

文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式编程错误处理机制设计

    本文主要总结嵌入式系统C语言编程中,主要的错误处理方式。文中涉及的代码运行环境如下。
    发表于 04-28 09:59 549次阅读
    嵌入式编程<b class='flag-5'>错误处理</b>机制设计

    嵌入式系统C语言编程中主要的错误处理方式

    本文主要总结嵌入式系统C语言编程中,主要的错误处理方式。
    发表于 07-24 16:40 550次阅读
    嵌入式系统C语言编程中主要的<b class='flag-5'>错误处理</b>方式

    Rust语言中错误处理的机制

    Rust语言中,错误处理是一项非常重要的任务。由于Rust语言采用静态类型检查,在编译时就能发现很多潜在的错误,这使得程序员能够更加自信和高效地开发程序。然而,即使我们在编译时尽可能
    的头像 发表于 09-19 14:54 785次阅读

    labviEW错误处理的问题

    为什么这个程序在启用自动错误处理和C:\data.txt不存在的情况下,没有显示错误对话框啊?
    发表于 04-01 10:03

    LabVIEW错误处理问题

    我想问一下,就是连接硬件采集波形时,需要滤掉直流波,但是采集到的波形时断断续续的,所以错误处理时会停止程序,我想问一下,运行时怎么忽略掉这个错误
    发表于 09-18 18:29

    原创|高速PCB设计中网表导入的常见错误处理

    Footprint,是由于原理图中信息缺少造成的。如下图:网表常见错误处理12、器件位号定义重复,如下图:网表常见错误处理23、器件位号信息缺失,如下图:网表常见错误处理3在原理图设计工具
    发表于 01-10 10:51

    AF错误处理

    想问一下关于AF的错误处理,例如我进行串口通讯,打开串口错误,但是我不想停止AF,想继续尝试连接要怎么做?
    发表于 02-03 15:44

    LabVIEW中的错误处理

    如何合理使用 LabVIEW 中的自定义错误处理功能;对于可预见的错误,是否可以选择直 接忽略,或者前几次尝试忽略直到该特定错误出现很多次后才通知用户需要纠正该错误 了;是否可以对
    发表于 05-24 11:07 6次下载

    Spring Boot框架错误处理

    》 《strong》翻译《/strong》:雁惊寒《/p》 《/blockquote》《p》《em》摘要:本文通过实例介绍了使用Spring Boot在设计API的时候如何正确地对异常进行处理。以下是译文《/em》《/p》《p》API在提供错误消息的同时进行适当的
    发表于 09-28 15:31 0次下载

    嵌入式系统C语言编程中的错误处理资料总结

    本文主要总结嵌入式系统C语言编程中,主要的错误处理方式。文中涉及的代码运行环境如下:
    发表于 11-28 10:39 1758次阅读

    Rust代码启发之返回值异常错误处理

    这样的代码,错误处理代码和业务逻辑交织在一起,也容易忽略处理错误。以及把返回值只用于错误返回,有点浪费的感觉。因为很多时候把计算结果作为返回值,更符合思考的逻辑。
    的头像 发表于 09-22 09:24 1658次阅读
    <b class='flag-5'>Rust</b>代码启发之返回值异常<b class='flag-5'>错误处理</b>

    rust语言基础学习: rust中的错误处理

    错误是软件中不可避免的,所以 Rust 有一些处理出错情况的特性。在许多情况下,Rust 要求你承认错误的可能性,并在你的代码编译前采取一些
    的头像 发表于 05-22 16:28 1410次阅读

    RS232通信时怎么处理错误?RS232通信中的错误处理方法

    RS232通信时怎么处理错误?RS232通信中的错误处理方法  RS232通信是一种电气标准,它定义了计算机和串行通信设备之间的通信协议。尽管RS232通信很稳定,但仍然可能会出现
    的头像 发表于 10-17 16:33 1835次阅读

    西门子博图:错误处理机制概览

    可通过以下几种不同的错误处理机制进行参数跟踪或编程或访问错误
    的头像 发表于 11-25 11:35 1027次阅读
    西门子博图:<b class='flag-5'>错误处理</b>机制概览

    C语言中的错误处理机制解析

    C 语言不提供对错误处理的直接支持,但是作为一种系统编程语言,它以返回值的形式允许您访问底层数据。
    的头像 发表于 02-26 11:19 227次阅读