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 编译并运行。 图…

有哪些搭建代理服务器的好方法?--代理IP小课堂

在互联网技术日益成熟的今天&#xff0c;代理服务器在提升网络安全性、提高访问速度以及数据抓取等方面发挥着越来越重要的作用。无论是大型企业还是个人用户&#xff0c;都可能会考虑搭建自己的代理服务器来满足特定的网络需求。今天我们就来说一说&#xff0c;要如何搭建代理…

千帆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->…

人民币波幅大幅收窄,未来方向将如何发展?

过去一周&#xff0c;在岸与离岸人民币的汇率波动变得极为有限&#xff0c;单周振幅收窄至约100点&#xff0c;较前一周进一步缩窄。上周&#xff0c;在岸人民币对美元的汇率最终定格在7.1960&#xff0c;周涨幅仅为0.03%。 多位外资银行的交易员和策略师在接受记者采访时指出…

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

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

34、keyof类型操作符

文章目录 js中的Object.keys()ts中的keyof实践中有何用应用范围 ts中有很多的工具类型&#xff1a;Partial部分、Required必填项、Pick选择、Record 记录&#xff1b;keyof在其中有大量的运用 js中的Object.keys() const keys Object.keys({id: 12,name:z}) // [id, name]ts中…

数组----柔性数组

C99标准中&#xff0c;定义了一个关于数组的动态数组的概念&#xff0c;该数组可以根据实际需求来改变数组的长度&#xff0c;以实现柔性变化&#xff0c;这种数组也被称为柔性数组。 一.柔性数组的定义 在结构体中&#xff0c;最后一个元素为可变长度的数组&#xff0c;这个…

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…

990-41产品经理:Essential Skills for Written Communication 书面沟通的基本技能

What is written communication? 什么是书面沟通&#xff1f; In the age of information, there is simply too much to remember. A simple solution is to write it all down. Written communication definition Written communication is making use of the written word…

电时代来临:2024北京新能源锂电池技术展盛夏8月!

2024中国&#xff08;北京&#xff09;国际新能源电池技术及设备展览会 时间&#xff1a;2024年8月1-3日 地点&#xff1a;中国国际展览中心&#xff08;顺义馆&#xff09; 展会概况 受益于政策的支持&#xff0c;我国新能源汽车市场规模逐步扩大、渗透率逐步提高&#xf…

前段结课总结

一.HTML基础 1.标记的类型&#xff1a;单个标记和成对标记&#xff0c;单个标记例如<br />用来设置换行操作<hr />用来设置分割线的属性<link />用来进行连接外部的文件&#xff0c;例如HTML文档的CSS样式设置可以放在新建的CSS文件类型里面&#xff0c;通过…

用例找到请求超时的接口

方法1&#xff1a; 在调用requests.api方法时传入timeout参数&#xff0c;指定timeout超时时间 如timeout10&#xff0c;则接口请求超过10秒没有返回&#xff0c;则请求抛出异常&#xff0c;终止执行 Read timed out. (read timeout5) 方法2&#xff1a; 在接口调用后&#…

LeetCode2575. Find the Divisibility Array of a String

文章目录 一、题目二、题解 一、题目 You are given a 0-indexed string word of length n consisting of digits, and a positive integer m. The divisibility array div of word is an integer array of length n such that: div[i] 1 if the numeric value of word[0,……

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;…

在Linux/Ubuntu/Debian中计算MD5,SHA256的方法

MD5&#xff08;消息摘要算法 5&#xff09;和 SHA-256&#xff08;安全哈希算法 256 位&#xff09;等流行的哈希算法广泛用于从任意数据生成固定大小的哈希值或校验和。 以下是这些算法及其计算方式的简要概述&#xff1a; MD5&#xff08;消息摘要算法5&#xff09;&#x…