Rust 11:错误处理(Option<T>、Result<T,E>、panic、catch_unwind)
文章目录
- 几种不同的错误处理方式
- 使用返回值(错误码)
- 使用异常(Exception)
- 使用类型系统
- Option< T >
- Result< T, E >
- ?操作符
- 代码测试
- ?操作符的使用限制
- panic!和catch_unwind
错误处理机制的设计是程序架构设计中非常重要的一部分。任何可能的错误,只要没有得到妥善的处理,日后都可能成为系统的隐患。
程序中,错误处理的基本流程为:
- 当错误发生时,用适当的方式捕获这个错误;
- 对捕获的错误可以立即进行处理,也可以对错误进行传播(propagate),直到不得不处理的地方再处理。
- 最终,对内做好错误信息的日志记录,对外给用户展示友好的、易于理解问题所在的错误信息。
几种不同的错误处理方式
使用返回值(错误码)
以C语言为代表,使用函数返回值作为错误记录和传播的手段。
缺点是,开发者需要根据准确的文档,以显示编程的方式处理(或进一步传递)各种返回情况。
然而,不得忽视的一个事实是,代码更新后往往很难保持文档的实时准确更新。
使用异常(Exception)
以Java语言为代表。
程序中任何可能出错的地方,都可以抛出异常;而异常可以通过栈回溯(stack unwind
)被一层层自动传递,直到遇到捕获异常的地方,如果回溯到main
函数还无人捕获,程序就会异常退出。
引入异常来处理错误,其优点是:错误的产生和错误的处理完全被分隔开(关注点分离),调用者不必关心错误,而被调者也不强求调用者关心错误。
不过,使用异常也有缺点:
- 在需要打开/关闭资源的情况下,容易出现因为抛出异常而资源没有得到正确关闭和释放的问题。这一点需要编程时特别小心,而且处理起来比较繁琐。
- 异常容易被滥用。毕竟,使用异常的代价要远比处理返回值的代价高。
使用类型系统
Rust语言中,就主要使用类型系统来传播和处理错误。主要使用到内置的Option<T>
和Result<T,E>
类型。
同时,当出现比较严重、不可恢复的错误时,Rust支持用panic!
宏抛出异常,用来快速退出程序,或者进入外围的catch_unwind
代码中。
Option< T >
标准库中的定义:
pub enum Option<T> {None,Some(T),
}
Option<T>
是一个枚举类型,要么是Some<T>
,要么是None
。这能很好地表达有值和无值两种情况,避免出现Java中的NullPointerException
。
Result< T, E >
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
pub enum Result<T, E> {/// Contains the success valueOk(T),/// Contains the error valueErr(E),
}
Result<T,E>
是也一个枚举类型,要么是正常情况下的结果类型T,要么是错误情况下的结果类型E。
注意一下 #must_use
标注,编译器会对有带有此标注的所有类型做特殊检查:如果该类型对应的值没有在代码中显式地使用,编译器会给出有警告。这能提醒开发者需要将所有错误显示处理。
?操作符
为了让Result<T,E>类型的处理更加方便简洁,Rust中引入了?
操作符。
?操作符本质是一个类似如下代码的match匹配。
match result {Ok(v) => v,Err(e) => return Err(e.into())
}
也就是说:
- 如果
Result
是一个E类型
的错误值,则提前返回错误,结束当前函数。 - 如果
Result
是一个T类型
的正确值,则提取出值,方便后续进行链式调用。
代码测试
先来定义一个自己的错误类型:
struct MyError {code: i32, msg: String
}
use std::fmt::{Display,Debug,Formatter};
impl std::error::Error for MyError { }
impl Debug for MyError {fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {write!(f, "[{}]{}", self.code, self.msg)}
}
impl Display for MyError {fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {write!(f, "[{}]{}", self.code, self.msg)}
}
在main()
函数中测试:
fn main() -> Result<(), MyError> {let oneError: Result<(), MyError>= Err(MyError{code: -1,msg: "error".to_owned(),});// 问号操作符是针对Result<T,E>类型的一个语法糖// 本质是一个match匹配// 如果Result是一个E类型的错误值,则提前返回错误,结束当前函数// 如果Result是一个T类型的正确值,则提取出值,方便后续进行链式调用oneError?;Ok(())
}
?操作符的使用限制
- ?操作符只能使用在以Option或者Result作为返回值的函数体中。
- 如果要在main()中使用?操作符。那么首先是要求
main()
返回值是Option
或者Result
类型(满足第1条);其次,还要求返回值是要实现std::process::Termination trait
的类型。
rust文档:Termination
pub trait Termination {fn report(self) -> i32;
}
其中report()
方法返回一个i32
值,这与C语言中main()
函数要求返回一个整数值一致。
测试代码中的Result<(), MyError>
可以作为main函数的返回值类型,是因为标准库中有如下实现:
impl<E: fmt::Debug> Termination for Result<(), E> {fn report(self) -> i32 {match self {Ok(()) => ().report(),Err(err) => Err::<!, _>(err).report(),}}
}
panic!和catch_unwind
当出现比较严重、不可恢复的错误时,Rust支持用panic!
宏抛出异常,用来快速退出程序。
如果某些情况下,不希望程序内部的panic!导致程序退出,可以将程序代码包裹在一个闭包中,然后传入std::panic::catch_unwind
中执行。
use std::panic;
let result = panic::catch_unwind(||{println!("Hello");
});
println!("{}", result.is_ok());//truelet result = panic::catch_unwind(||{// 打印异常堆栈信息// 被catch_unwind包裹,该panic不会导致进程退出panic!("Error in catch_unwind");
});
if(result.is_err()) {// 发现异常println!("maybe do something");
}println!("before panic");//before panic
//让程序异常退出
panic!("error exit");
//上一句代码已导致程序异常退出,这一句执行不到
println!("normal exit");
Rust 11:错误处理(Option<T>、Result<T,E>、panic、catch_unwind)相关推荐
- 16.枚举中的option和result.rs
fn test_option() {//Option 是 Rust 标准库中的枚举类,这个类用于填补 Rust 不支持 null 引用的空白/*enum Option<T> {Some(T ...
- rust的错误和异常
一.错误和异常 在所有语言中,对程序运行不按照设计的"套路"出牌,都是错误.异常可以理解成程序的错误引发了运行的故障,甚至导致程序崩溃.正因为如此,对错误和异常的处理是所有语言都必 ...
- Rust之错误处理(三):panic!还是不要panic!
开发环境 Windows 10 Rust 1.68.0 VS Code 1.76.2 项目工程 这里继续沿用上次工程rust-demo panic!还是不要panic! 那么,你如何决定何时应该调用p ...
- ssm查询,错误Could not find result map cn.itcast.ssm.po.ItemsCustom
错误信息: 在查询列表是,报如下错误 Could not find result map cn.itcast.ssm.po.ItemsCustom org.apache.ibatis.builder. ...
- MySQL安装错误: unknown option '--skip-federated'
mysql启动时出现以下错误: [ERROR] /usr/local/mysql/libexec/mysqld: unknown option '--skip-federated' [ERROR] A ...
- rust:错误处理 Blocking waiting for file lock on package cache
编译程序的时候出现下面的错误信息: Blocking waiting for file lock on package cache 方法是,删除上面这个缓存文件.
- 记录Unity 打包APK 错误 The option ‘android.enableR8’ is deprecated and should not be used anymore.解决方案
版本 版本2020.2.6f1 打包安卓APK 时报错: Configure project :launcher WARNING: The option 'android.enableR8' is d ...
- rust(11)-函数作为返回值和match(类似于switch)
继续以解一元多次方程割线法为例 PS F:\learn\rustlearn> rustc learn2.rs PS F:\learn\rustlearn> .\learn2.exe fun ...
- Myeclipse使用DB Browser连接数据库错误:OPTION SQL_SELECT_LIMIT=DEFAULT
虽然使用Myeclipse,经过test driver可以使用, 但是不能够查询mysql数据库各个表的数据. 百度了下, 原来是驱动mysql的插件版本很低,重新下了个, 可以了. 下面是链接. ...
最新文章
- Pci设备驱动:设备枚举
- 单机搭建Android开发环境(五)
- 要闻君说:华为发布2018年年度报告:全球销售收入超千亿美元;微软”立誓“不过愚人节;大众与AWS一起做工业汽车云...
- Flutter TextField 设置默认值和光标位置
- 基于JAVA+Servlet+JSP+MYSQL的图书销售管理系统
- [转]Servlet 3.0 新特性详解
- hadoop mapper从源码开始 详解
- Pascal Sentences数据集预处理
- 数据的展现技巧——数据透视表(一)
- 学it需要学历吗_低学历者是否适合学IT?IT行业对学历要求高吗
- 中国电信 合约协议 转载
- NIKE的网络营销案例给我们的启发
- 进销存系统_用户信息更新密码修改(3)
- 开发一个项目需要的基本知识
- Unity UGUI 设置Dropdown的选项菜单显示在上面
- 中软JavaEE软件工程师培训有感
- Nginx负载均衡轮询访问只会刚开始轮询一次,以后一直访问同一服务器
- 懒人之家-QQ客服右侧
- Android Studio入门级UI界面设计(图文+解析)
- 已解决TypeError: __init__() got an unexpected keyword argument ‘n_iterations‘