Rust错误处理和Result枚举类异常错误传递

Rust 有一套独特的处理异常情况的机制,它并不像其它语言中的 try 机制那样简单。

首先,程序中一般会出现两种错误:可恢复错误和不可恢复错误。

可恢复错误的典型案例是文件访问错误,如果访问一个文件失败,有可能是因为它正在被占用,是正常的,我们可以通过等待来解决。

但还有一种错误是由编程中无法解决的逻辑错误导致的,例如访问数组末尾以外的位置。

大多数编程语言不区分这两种错误,并用 Exception (异常)类来表示错误。在 Rust 中没有 Exception。

对于可恢复错误用 Result<T, E> 类来处理,对于不可恢复错误使用 panic! 宏来处理。

不可恢复错误

本章以前没有专门介绍 Rust 宏的语法,但已经使用过了 println! 宏,因为这些宏的使用较为简单,所以暂时不需要彻底掌握它,我们可以用同样的方法先学会使用 panic! 宏的使用方法。

实例

fn main() {panic!("error occured");println!("Hello, Rust");
}

运行结果:

thread 'main' panicked at 'error occured', src\main.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

很显然,程序并不能如约运行到 println!("Hello, Rust") ,而是在 panic! 宏被调用时停止了运行。

不可恢复的错误一定会导致程序受到致命的打击而终止运行。

让我们注视错误输出的两行:

  • 第一行输出了 panic! 宏调用的位置以及其输出的错误信息。
  • 第二行是一句提示,翻译成中文就是"通过 `RUST_BACKTRACE=1` 环境变量运行以显示回溯"。接下来我们将介绍回溯(backtrace)。

紧接着刚才的例子,我们在 VSCode 中新建一个终端:

在新建的终端里设置环境变量(不同的终端方法不同,这里介绍两种主要的方法):

如果在 Windows 7 及以上的 Windows 系统版本中,默认使用的终端命令行是 Powershell,请使用以下命令:

$env:RUST_BACKTRACE=1 ; cargo run

如果你使用的是 Linux 或 macOS 等 UNIX 系统,一般情况下默认使用的是 bash 命令行,请使用以下命令:

RUST_BACKTRACE=1 cargo run

然后,你会看到以下文字:

thread 'main' panicked at 'error occured', src\main.rs:3:5
stack backtrace:...11: greeting::mainat .\src\main.rs:3...

回溯是不可恢复错误的另一种处理方式,它会展开运行的栈并输出所有的信息,然后程序依然会退出。上面的省略号省略了大量的输出信息,我们可以找到我们编写的 panic! 宏触发的错误。

可恢复的错误

此概念十分类似于 Java 编程语言中的异常。实际上在 C 语言中我们就常常将函数返回值设置成整数来表达函数遇到的错误,在 Rust 中通过 Result<T, E> 枚举类作返回值来进行异常表达:

enum Result<T, E> {Ok(T),Err(E),
}

在 Rust 标准库中可能产生异常的函数的返回值都是 Result 类型的。例如:当我们尝试打开一个文件时:

实例

use std::fs::File;fn main() {let f = File::open("hello.txt");match f {Ok(file) => {println!("File opened successfully.");},Err(err) => {println!("Failed to open the file.");}}
}

如果 hello.txt 文件不存在,会打印 "Failed to open the file."

当然,我们在枚举类章节讲到的 if let 语法可以简化 match 语法块:

实例

use std::fs::File;fn main() {let f = File::open("hello.txt");if let Ok(file) = f {println!("File opened successfully.");} else {println!("Failed to open the file.");}
}

如果想使一个可恢复错误按不可恢复错误处理,Result 类提供了两个办法:unwrap() 和 expect(message: &str) :

实例

use std::fs::File;fn main() {let f1 = File::open("hello.txt").unwrap();let f2 = File::open("hello.txt").expect("Failed to open.");
}

这段程序相当于在 Result 为 Err 时调用 panic! 宏。两者的区别在于 expect 能够向 panic! 宏发送一段指定的错误信息。

可恢复的错误的传递

之前所讲的是接收到错误的处理方式,但是如果我们自己编写一个函数在遇到错误时想传递出去怎么办呢?

实例

fn f(i: i32) -> Result<i32, bool> {if i >= 0 { Ok(i) }else { Err(false) }
}fn main() {let r = f(10000);if let Ok(v) = r {println!("Ok: f(-1) = {}", v);} else {println!("Err");}
}

运行结果:

Ok: f(-1) = 10000

这段程序中函数 f 是错误的根源,现在我们再写一个传递错误的函数 g :

实例

fn g(i: i32) -> Result<i32, bool> {let t = f(i);return match t {Ok(i) => Ok(i),Err(b) => Err(b)};
}

函数 g 传递了函数 f 可能出现的错误(这里的 g 只是一个简单的例子,实际上传递错误的函数一般还包含很多其它操作)。

这样写有些冗长,Rust 中可以在 Result 对象后添加 ? 操作符将同类的 Err 直接传递出去:

实例

fn f(i: i32) -> Result<i32, bool> {if i >= 0 { Ok(i) }else { Err(false) }
}fn g(i: i32) -> Result<i32, bool> {let t = f(i)?;Ok(t) // 因为确定 t 不是 Err, t 在这里已经是 i32 类型
}fn main() {let r = g(10000);if let Ok(v) = r {println!("Ok: g(10000) = {}", v);} else {println!("Err");}
}

运行结果:

Ok: g(10000) = 10000

? 符的实际作用是将 Result 类非异常的值直接取出,如果有异常就将异常 Result 返回出去。所以,? 符仅用于返回值类型为 Result<T, E> 的函数,其中 E 类型必须和 ? 所处理的 Result 的 E 类型一致。


kind 方法

到此为止,Rust 似乎没有像 try 块一样可以令任何位置发生的同类异常都直接得到相同的解决的语法,但这样并不意味着 Rust 实现不了:我们完全可以把 try 块在独立的函数中实现,将所有的异常都传递出去解决。实际上这才是一个分化良好的程序应当遵循的编程方法:应该注重独立功能的完整性。

但是这样需要判断 Result 的 Err 类型,获取 Err 类型的函数是 kind()。

实例

use std::io;
use std::io::Read;
use std::fs::File;fn read_text_from_file(path: &str) -> Result<String, io::Error> {let mut f = File::open(path)?;let mut s = String::new();f.read_to_string(&mut s)?;Ok(s)
}fn main() {let str_file = read_text_from_file("hello.txt");match str_file {Ok(s) => println!("{}", s),Err(e) => {match e.kind() {io::ErrorKind::NotFound => {println!("No such file");},_ => {println!("Cannot read the file");}}}}
}

运行结果:

No such file

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/727280.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

乔琼:高性能会议传声器的产品优化设计| 演讲嘉宾公布

一、智能家居与会议系统 智能家居与会议系统分论坛将于3月28日同期举办&#xff01; 智能会议系统它通过先进的技术手段&#xff0c;提高了会议效率&#xff0c;降低了沟通成本&#xff0c;提升了参会者的会议体验。对于现代企业、政府机构和学术界是不可或缺的。在这里&#x…

arm系统构建的基础知识

目录 一、环境变量 二、归档和压缩 (一) 常用命令 (二) 常用参数 三、磁盘分区和挂载 四、网络管理 一、环境变量 显示环境变量 —— echo设置临时环境变量 —— exportecho $PATH —— 显示当前PATH环境变量 在当前目录下&#xff0c;编写一个hello.c 编译并运行。 图…

千帆AppBuilder使用指南-组件中心

应用中心 百度智能云千帆AppBuilder&#xff08;以下简称为AppBuilder&#xff09;应用中心&#xff0c;提供了大量可以立即体验的应用示例&#xff0c;开发者可以在这里搜索感兴趣的应用进行使用。 官方应用&#xff1a;AppBuilder官方提供的应用&#xff0c;可以立即体验应用…

【nowcoder】NC248 左叶子之和

NC248 左叶子之和 计算给定二叉树的左叶子之和。 树上叶子节点指没有后继节点的节点&#xff0c;左叶子指连向父节点的左侧的叶子节点。 int sumOfLeftLeaves(struct TreeNode* root ) {if (root ! NULL) {int sum 0;if (root->left ! NULL && root->left->…

【CSP试题回顾】201409-3-字符串匹配

CSP-201409-3-字符串匹配 关键点&#xff1a;<string>库函数的使用 length() 或 size(): 返回字符串的长度。 empty(): 检查字符串是否为空。 append() 或 : 向字符串的末尾添加字符或另一个字符串。 insert()在字符串的指定位置插入另一个字符串或字符。 std::str…

redis未设置密码被植入挖矿脚本

最近一台测试linux响应速度贼慢&#xff0c;检查发现cpu消耗高达100%&#xff01;查看进程杀死后过段时间又重启了&#xff0c;一时间也摸不到头绪。无意间发现启动redis的时候cpu瞬间拉到了100%&#xff0c;主要就是zzh和newinit.sh两个脚本。百度了一下说是被植入了挖矿脚本&…

玩转安卓之配置gradle-8.2.1

概述&#xff1a;看了一下&#xff0c;由于gradle是国外的&#xff0c;所以下载速度很慢&#xff0c;这个老师又是很菜的类型&#xff0c;同学又不会&#xff0c;于是曹某就写这一篇文章&#xff0c;教大家学会简单的为安卓配置gradle-8.2.1。 第一步&#xff1a;下载gradle-8…

C++复习笔记——泛型编程模板

01 模板 模板就是建立通用的模具&#xff0c;大大提高复用性&#xff1b; 02 函数模板 C另一种编程思想称为 泛型编程 &#xff0c;主要利用的技术就是模板 C 提供两种模板机制:函数模板和类模板 函数模板语法 函数模板作用&#xff1a; 建立一个通用函数&#xff0c;其函…

【kubernetes】关于k8s集群的配置资源(configmap和secret)

目录 一、Secret 类型一&#xff1a;kubernetes.io/service-account-token 类型二&#xff1a;普通类型secret&#xff0c; ●Opaque&#xff0c;base64 编码格式的 Secret&#xff0c;用来存储用户自定义的密码、密钥等&#xff0c;默认的 Secret 类型; 类型三&#xff1a;…

tiktok矩阵引流系统开发常用源代码!

在数字营销领域&#xff0c;TikTok已成为一个不可忽视的平台&#xff0c;随着其用户基数的不断增长&#xff0c;如何利用TikTok进行有效的引流成为了许多企业和营销人员关注的焦点。 为了实现这一目标&#xff0c;许多开发者开始构建TikTok矩阵引流系统&#xff0c;这些系统通…

基于docker安装的Jenkins实现python执行自动化测试程序

背景 通过Jenkins实现自动化测试,在全局配置中配置好后,执行构建发生如下错误 解决办法: 在Jenkins中插件管理中下载python后,回到Jenkins容器中 查找刚下载的python所在位置 到Jenkins中全局配置中修改脚本 1.可以在环境变量中定义python所在位置 2.在一下图示中进行获取…

数据解读乡村发展!专家详解 2024 年(第 17 届)中国大学生计算机设计大赛大数据主题赛赛题

2024 年&#xff08;第 17 届&#xff09;中国大学生计算机设计大赛大数据主题赛“数据解读乡村发展”赛题已于和鲸平台正式开赛&#xff0c;一月来&#xff0c;已有来自全国超百所高校的优秀本科生积极响应大赛号召完成报名。 为进一步使广大师生对于赛题主旨形成更清晰的认知…

尝试各种可以联网的AI

phind https://www.phind.com/ 搜索次数限制&#xff1a;无 右侧显示搜索引用文章地址链接以及大致内容缩略显示 左侧显示总结 perplexity https://www.perplexity.ai/ 搜索次数限制&#xff1a;每四小时5次搜索 内容上下分层&#xff0c;样式如下&#xff1a; Sources 显示…

一篇长文教你进行全方位的使用appium【建议收藏】

随着移动应用的日益普及&#xff0c;移动应用的测试成为了软件开发的重要组成部分。Python&#xff0c;作为一种易于学习&#xff0c;功能强大的编程语言&#xff0c;特别适合进行这种测试。本文将详细介绍如何使用Python进行APP测试&#xff0c;并附带一个实例。 Python 和 A…

OPCUA 学习笔记-程序模型

无论是边缘控制器&#xff0c;还是PLC 中&#xff0c;除了信息模型之外&#xff0c;还有应用程序&#xff0c;这些程序可能是IEC61131-3 编写的程序&#xff0c;也可能是其它程序开发的可执行程序。 尽管OPCUA 描述模型能力很强&#xff0c;但是它缺乏算法的描述方式。但是OPCU…

Python与FPGA——全局二值化

文章目录 前言一、Python全局128二、Python全局均值三、Python全局OTSU四、FPGA全局128总结 前言 为什么要进行图像二值化&#xff0c;rgb图像有三个通道&#xff0c;处理图像的计算量较大&#xff0c;二值化的图像极大的减少了处理图像的计算量。即便从彩色图像转成了二值化图…

linux系统---selinux

目录 前言 一、SELinux 的作用及权限管理机制 1.SELinux 的作用 1.1DAC 1.2MAC 1.3DAC 和 MAC 的对比 2.SELinux 基本概念 2.1主体&#xff08;Subject&#xff09; 2.2对象&#xff08;Object&#xff09; 2.3政策和规则&#xff08;Policy & Rule&#xff09; …

强化学习中动作价值函数和状态价值函数的联系区别?

在强化学习中&#xff0c;动作价值函数&#xff08;Q函数&#xff09;和状态价值函数&#xff08;V函数&#xff09;都是值函数&#xff0c;用于评估在不同状态或状态动作对下的值。它们之间存在联系&#xff0c;但有一些区别&#xff1a; 动作价值函数&#xff08;Q函数&#…

Web自动化测试框架-PO模式

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

Python接口自动化之cookie、session应用!

以下介绍cookie、session原理及在接口自动化中的应用。 HTTP 协议是一种无状态协议&#xff0c;即每次服务端接收到客户端的请求时&#xff0c;都是一个全新的请求&#xff0c;服务器并不知道客户端的历史请求记录&#xff1b;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的…