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

news2025/1/22 9:14:48

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::main
             at .\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.coloradmin.cn/o/1495690.html

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

相关文章

初学C++

注释 变量 作用&#xff1a;给一段指定的内存空间起名&#xff0c;方便操作这段内容 数据类型 变量名 变量初始值; 常量 用于记录程序中不可更改的数据 宏常量&#xff1a; #define 宏常量 常量值 const修饰的变量&#xff1a; const 数据类型 常量名 常量值; 关键字 …

C#,回文分割问题(Palindrome Partitioning Problem)算法与源代码

1 回文串 “回文串”是一个正读和反读都一样的字符串&#xff0c;初始化标志flagtrue&#xff0c;比如“level”或者“noon”等等就是回文串。 2 回文分割问题 给定一个字符串&#xff0c;如果该字符串的每个子字符串都是回文的&#xff0c;那么该字符串的分区就是回文分区。…

【EI会议征稿通知】2024年计算机视觉、机器人与自动化工程国际学术会议(CRAE 2024)

2024年计算机视觉、机器人与自动化工程国际学术会议&#xff08;CRAE 2024&#xff09; 2024 International Conference on Computer Vision, Robotics and Automation Engineering&#xff08;CRAE 2024&#xff09; 2024年计算机视觉、机器人与自动化工程国际学术会议&…

华为配置智能升级功能升级设备示例

配置智能升级功能升级设备示例 组网图形 图1 配置智能升级功能组网图 背景信息组网需求配置思路前提条件操作步骤操作结果 背景信息 为了方便用户及时了解设备主流运行版本&#xff0c;快速完成升级修复&#xff0c;华为设备支持自动下载、自助升级功能。用户在设备Web网管…

精细调度:Apache DolphinScheduler脚本深度解析

在现代数据处理和工作流管理中&#xff0c;Apache DolphinScheduler以其灵活性和强大的调度能力受到开发者的广泛欢迎。 本文将逐步解析DolphinScheduler的关键脚本&#xff0c;希望能提供一个详尽的操作指南&#xff0c;帮助大家掌握安装、配置和操作的每一步。 建立在./bin/e…

基于机器学习的垃圾分类

1绪论 1.1问题背景 垃圾分类有减少环境污染、节省土地资源、再生资源的利用、提高民众价值观念等的好处&#xff0c;在倡导绿色生活&#xff0c;注重环境保护的今天&#xff0c;正确的垃圾分类和处理对我们的生态环境显得尤为重要。 在国外很多国家&#xff0c;经过了几十年…

解决WordPress更新插件或者更新版本报WordPress 需要访问您网页服务器的权限的问题

文章目录 前言一、原因二、解决步骤总结 前言 当对WordPress的插件或者版本进行更新时报错&#xff1a;要执行请求的操作&#xff0c;WordPress 需要访问您网页服务器的权限。 请输入您的 FTP 登录凭据以继续。 如果您忘记了您的登录凭据&#xff08;如用户名、密码&#xff09…

JAVA 用二分法查找数组中是否存在某个值

二分法查找的概念 二分查找也称折半查找&#xff08;Binary Search&#xff09;&#xff0c;它是一种效率较高的查找方法。首先&#xff0c;将表中间位置记录的关键字与查找关键字比较&#xff0c;如果两者相等&#xff0c;则查找成功&#xff1b;否则利用中间位置记录将表分成…

每日一题——LeetCode1588.所有奇数长度子数组的和

方法一 暴力循环 遍历数组的每一个元素&#xff0c;找寻该元素所有连续长度为奇数的可能&#xff0c;累加奇数长度区间内的所有元素。 比如对于[1,4,2,5,3] &#xff0c;对于第一个元素1&#xff0c;有[1]、[1,4,2]、[1,4,2,5,3]这三种可能&#xff0c;对于第二个元素4&#x…

STP---生成树协议

STP的作用 a)Stp通过阻塞端口来消除环路&#xff0c;并能够实现链路备份目的 b)消除了广播风暴 c)物理链路冗余&#xff0c;网络变成了层次化结构的网络 STP操作 选举一个根桥每个非根交换机选举一个根端口每个网段选举一个指定端口阻塞非根&#xff0c;非指定端口 STP--生成树…

Java 数据结构之链表

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA null || headB null) return null;ListNode pA headA, pB headB;while (pA ! pB) {pA pA null ? headB : pA.next;pB pB null ? headA : pB.next;}return pA;} public ListNode rev…

Linux cgrpup技术解析和验证测试

Linux cgrpup技术解析和验证测试 1. cgroup技术解析和分类1.1. 相关概念介绍1.2 cgroup子系统 2. cgroup子系统详解2.1 cpu子系统2.2 cpuacct子系统2.3 cpuset子系统2.4 memory子系统2.5 blkio子系统2.6 ns子系统 3. cgroup使用3.1 通用使用流程3.1.1 限制进程的cpu资源3.1.2 绑…

【开源项目】ollama:本地部署大模型

文章目录 1. 安装2. 使用体验2.1. 运行llama22.2. 运行llama2-chinese 项目地址&#xff1a;Github - ollama/ollama 注意&#xff1a;项目对硬盘容量、内存要求比较高。 1. 安装 从Github项目的最新release下载安装包&#xff0c;点击运行即可。 2. 使用体验 2.1. 运行ll…

中小企业如何降低网络攻击和数据泄露的风险?

德迅云安全收集了Bleeping Computer 网站消息&#xff0c; Arctic Wolf 表示 Akira 勒索软件组织的攻击目标瞄准了中小型企业&#xff0c;自 2023 年 3 月以来&#xff0c;该团伙成功入侵了多家组织&#xff0c;索要的赎金从 20 万美元到 400 多万美元不等&#xff0c;如果受害…

基于OpenCV的图形分析辨认05(补充)

目录 一、前言 二、实验内容 三、实验过程 一、前言 编程语言&#xff1a;Python&#xff0c;编程软件&#xff1a;vscode或pycharm&#xff0c;必备的第三方库&#xff1a;OpenCV&#xff0c;numpy&#xff0c;matplotlib&#xff0c;os等等。 关于OpenCV&#xff0c;num…

常见排序算法解析

芝兰生于深林&#xff0c;不以无人而不芳&#xff1b;君子修道立德&#xff0c;不为穷困而改节 文章目录 插入排序直接插入排序希尔排序 选择排序直接选择排序堆排序 交换排序冒泡排序快速排序优化挖坑法前后指针法非递归版 归并排序递归非递归 总结 插入排序 插入排序&#…

论文阅读笔记 | MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment

文章目录 文章题目发表年限期刊/会议名称论文简要动机主要思想或方法架构实验结果 文章链接&#xff1a;https://doi.org/10.48550/arXiv.2004.05508 文章题目 MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment 发表年限 2020 期刊/会议名称 Publi…

Unity性能优化篇(八) 导入的模型网格优化设置

模型导入Unity后&#xff0c;可以选中这个模型&#xff0c;在Inspector窗口设置它的属性。下面说的都是可自定义选择优化的地方 Model选择卡: 1.在Model选项卡&#xff0c;启用Mesh Compression可以压缩模型&#xff0c;压缩程度越高&#xff0c;模型精度越低&#xff0c;但是…

Docker前后端项目部署

目录 一、搭建项目部署的局域网 二、redis安装 三、MySQL安装 四、若依后端项目搭建 4.1 使用Dockerfile自定义镜像 五、若依前端项目搭建 一、搭建项目部署的局域网 搭建net-ry局域网&#xff0c;用于部署若依项目 docker network create net-ry --subnet172.68.0.0/1…

网络安全审计是什么意思?与等保测评有什么区别?

网络安全审计和等保测评在信息安全领域中都是非常重要的环节。但不少人对于这两者是傻傻分不清楚&#xff0c;今天我们就来简单聊聊网络安全审计是什么意思&#xff1f;与等保测评有什么区别&#xff1f; 网络安全审计是什么意思&#xff1f; 网络安全审计是通过对网络系统和网…