文章目录

  • 几种不同的错误处理方式
    • 使用返回值(错误码)
    • 使用异常(Exception)
    • 使用类型系统
  • Option< T >
  • Result< T, E >
  • ?操作符
    • 代码测试
    • ?操作符的使用限制
  • panic!和catch_unwind

错误处理机制的设计是程序架构设计中非常重要的一部分。任何可能的错误,只要没有得到妥善的处理,日后都可能成为系统的隐患。

程序中,错误处理的基本流程为:

  1. 当错误发生时,用适当的方式捕获这个错误;
  2. 对捕获的错误可以立即进行处理,也可以对错误进行传播(propagate),直到不得不处理的地方再处理。
  3. 最终,对内做好错误信息的日志记录,对外给用户展示友好的、易于理解问题所在的错误信息。

几种不同的错误处理方式

使用返回值(错误码)

以C语言为代表,使用函数返回值作为错误记录和传播的手段。
缺点是,开发者需要根据准确的文档,以显示编程的方式处理(或进一步传递)各种返回情况。
然而,不得忽视的一个事实是,代码更新后往往很难保持文档的实时准确更新

使用异常(Exception)

以Java语言为代表。
程序中任何可能出错的地方,都可以抛出异常;而异常可以通过栈回溯(stack unwind)被一层层自动传递,直到遇到捕获异常的地方,如果回溯到main函数还无人捕获,程序就会异常退出。
引入异常来处理错误,其优点是:错误的产生和错误的处理完全被分隔开(关注点分离),调用者不必关心错误,而被调者也不强求调用者关心错误。

不过,使用异常也有缺点:

  1. 在需要打开/关闭资源的情况下,容易出现因为抛出异常而资源没有得到正确关闭和释放的问题。这一点需要编程时特别小心,而且处理起来比较繁琐。
  2. 异常容易被滥用。毕竟,使用异常的代价要远比处理返回值的代价高。

使用类型系统

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

?操作符的使用限制

  1. ?操作符只能使用在以Option或者Result作为返回值的函数体中
  2. 如果要在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)相关推荐

  1. 16.枚举中的option和result.rs

    fn test_option() {//Option 是 Rust 标准库中的枚举类,这个类用于填补 Rust 不支持 null 引用的空白/*enum Option<T> {Some(T ...

  2. rust的错误和异常

    一.错误和异常 在所有语言中,对程序运行不按照设计的"套路"出牌,都是错误.异常可以理解成程序的错误引发了运行的故障,甚至导致程序崩溃.正因为如此,对错误和异常的处理是所有语言都必 ...

  3. Rust之错误处理(三):panic!还是不要panic!

    开发环境 Windows 10 Rust 1.68.0 VS Code 1.76.2 项目工程 这里继续沿用上次工程rust-demo panic!还是不要panic! 那么,你如何决定何时应该调用p ...

  4. 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. ...

  5. MySQL安装错误: unknown option '--skip-federated'

    mysql启动时出现以下错误: [ERROR] /usr/local/mysql/libexec/mysqld: unknown option '--skip-federated' [ERROR] A ...

  6. rust:错误处理 Blocking waiting for file lock on package cache

    编译程序的时候出现下面的错误信息: Blocking waiting for file lock on package cache 方法是,删除上面这个缓存文件.

  7. 记录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 ...

  8. rust(11)-函数作为返回值和match(类似于switch)

    继续以解一元多次方程割线法为例 PS F:\learn\rustlearn> rustc learn2.rs PS F:\learn\rustlearn> .\learn2.exe fun ...

  9. Myeclipse使用DB Browser连接数据库错误:OPTION SQL_SELECT_LIMIT=DEFAULT

    虽然使用Myeclipse,经过test driver可以使用, 但是不能够查询mysql数据库各个表的数据.  百度了下, 原来是驱动mysql的插件版本很低,重新下了个, 可以了. 下面是链接. ...

最新文章

  1. Pci设备驱动:设备枚举
  2. 单机搭建Android开发环境(五)
  3. 要闻君说:华为发布2018年年度报告:全球销售收入超千亿美元;微软”立誓“不过愚人节;大众与AWS一起做工业汽车云...
  4. Flutter TextField 设置默认值和光标位置
  5. 基于JAVA+Servlet+JSP+MYSQL的图书销售管理系统
  6. [转]Servlet 3.0 新特性详解
  7. hadoop mapper从源码开始 详解
  8. Pascal Sentences数据集预处理
  9. 数据的展现技巧——数据透视表(一)
  10. 学it需要学历吗_低学历者是否适合学IT?IT行业对学历要求高吗
  11. 中国电信 合约协议   转载
  12. NIKE的网络营销案例给我们的启发
  13. 进销存系统_用户信息更新密码修改(3)
  14. 开发一个项目需要的基本知识
  15. Unity UGUI 设置Dropdown的选项菜单显示在上面
  16. 中软JavaEE软件工程师培训有感
  17. Nginx负载均衡轮询访问只会刚开始轮询一次,以后一直访问同一服务器
  18. 懒人之家-QQ客服右侧
  19. Android Studio入门级UI界面设计(图文+解析)
  20. 已解决TypeError: __init__() got an unexpected keyword argument ‘n_iterations‘

热门文章

  1. Unity ~2D精灵动画制作
  2. mysql创建数据库名字中带 - 的方式
  3. Java 8 Optional:优雅地避免 NPE
  4. 理解 GNU Libtool
  5. 湖南计算机专业公办学校,长沙现代应用学校计算机网络应用专业|公办
  6. Vue 数组嵌套中循环数组改变值未响应
  7. 学习java之路之第二周
  8. Markdown教程(Typora使用教程)
  9. PhotoShop学习笔记(一):PhotoShop获得商品背景为透明图的图片
  10. 英语给你带来的全是惊喜