Rust之错误处理(一):无法恢复的错误panic!

news2024/11/19 8:46:37

开发环境

  • Windows 10
  • Rust 1.66.1

 

  • VS Code 1.74.3

  项目工程

这里继续沿用上次工程rust-demo

错误处理

错误是软件生活中的一个事实,所以Rust有一些处理出错情况的功能。在许多情况下,Rust要求你承认错误的可能性,并在你的代码编译前采取一些行动。这一要求使你的程序更加健壮,因为它可以确保你在将代码部署到生产中之前就能发现错误并进行适当的处理。

Rust将错误分为两大类:可恢复的和不可恢复的错误。对于一个可恢复的错误,比如文件未找到的错误,我们很可能只想向用户报告问题并重试操作。不可恢复的错误总是错误的症状,比如试图访问一个超过数组末端的位置,因此我们要立即停止程序。

大多数语言不区分这两种错误,并以同样的方式处理这两种错误,使用异常等机制。Rust没有异常。相反,它有Result<T, E>类型,用于处理可恢复的错误,还有panic!宏,在程序遇到不可恢复的错误时停止执行。本章首先涉及调用panic!,然后讨论返回Result<T, E>值。此外,我们将探讨在决定是尝试从错误中恢复还是停止执行时的注意事项。

无法恢复的错误:panic!

有时,在你的代码中会发生不好的事情,而你却无能为力。在这种情况下,Rust有panic!宏。在实践中,有两种方法可以导致恐慌:通过采取导致我们的代码恐慌的行动(比如访问一个超过终点的数组),或者明确地调用panic!宏。在这两种情况下,我们都会在我们的程序中引起恐慌。默认情况下,这些恐慌会打印一个失败的消息,解开,清理堆栈,然后退出。通过环境变量,你也可以让Rust在恐慌发生时显示调用堆栈,以便更容易追踪到恐慌的来源。

解除堆栈或终止对恐慌的反应

默认情况下,当恐慌发生时,程序会开始解开,这意味着Rust会向后移动堆栈,清理它遇到的每个函数的数据。然而,这种回走和清理是一个很大的工作。因此,Rust允许你选择立即中止,即在不进行清理的情况下结束程序。

程序所使用的内存将需要由操作系统来清理。如果在你的项目中,你需要使生成的二进制文件尽可能的小,你可以通过在Cargo.toml文件中适当的[profile]部分添加panic = 'abort',将解卷转换为恐慌时中止。例如,如果你想在发布模式下,在恐慌时中止,请添加这个。

[profile.release]
panic = 'abort'

让我们试着在一个简单的程序中调用panic! 

文件名: src/main.rs 

fn main() {
    panic!("crash and burn");      // panic!宏
}

运行

cargo run

 对panic!的调用导致了最后两行中包含的错误信息。第一行显示了我们的恐慌信息和源代码中发生恐慌的地方: src/main.rs:914:5 表示这是我们 src/main.rs 文件的第二行,第五个字符。

在这种情况下,指示的行是我们代码的一部分,如果我们去看那一行,我们就会看到panic!宏的调用。在其他情况下,panic!调用可能在我们的代码所调用的代码中,而错误信息所报告的文件名和行号将是别人的代码,其中调用了panic!宏,而不是我们的代码中最终导致panic!调用的那一行。我们可以使用panic!调用所来自的函数的回溯,来找出我们代码中导致问题的部分。接下来我们将更详细地讨论回溯。 

使用panic! 溯源

让我们看看另一个例子,看看当panic!调用来自于一个库,因为我们的代码中有一个bug,而不是来自于我们的代码直接调用宏时,会是什么样子。下例中有一些代码试图访问一个向量中超出有效索引范围的索引。

文件名: src/main.rs

fn main() {
    let v = vec![1, 2, 3];

    v[99];        // 向量越界访问
}

在这里,我们试图访问我们的向量的第100个元素(它在索引99处,因为索引从0开始),但向量只有3个元素。在这种情况下,Rust会出现恐慌错误。使用[]应该返回一个元素,但是如果你传递了一个无效的索引,那么Rust在这里就没有任何元素可以正确返回。

在C语言中,试图读取一个数据结构的末端是未定义的行为。你可能会得到内存中对应于数据结构中该元素的任何位置,即使该内存不属于该结构。这被称为缓冲区超读,如果攻击者能够以这样的方式操纵索引,从而读取他们不应该被允许的、存储在数据结构之后的数据,就会导致安全漏洞。

为了保护你的程序不受这种漏洞的影响,如果你试图在一个不存在的索引上读取一个元素,Rust会停止执行并拒绝继续。 

运行

cargo run

 这个错误指向main.rs的第920行,在那里我们试图访问索引99。 下一行注释告诉我们,我们可以设置RUST_BACKTRACE环境变量,以获得导致错误的确切情况的回溯。回溯是一个所有被调用的函数的列表,以达到这一点。Rust中的回溯和其他语言中的回溯一样:阅读回溯的关键是从顶部开始阅读,直到看到你写的文件。这就是问题的根源所在。在这个位置上面的行是你的代码所调用的代码;下面的行是调用你的代码的代码。这些前后的行可能包括Rust核心代码、标准库代码或你正在使用的crates。让我们尝试通过设置RUST_BACKTRACE环境变量为0以外的任何值来获取回溯。

使用下述指令运行

RUST_BACKTRACE=1 cargo run

 这是个很多的输出! 你看到的确切输出可能会有所不同,这取决于你的操作系统和Rust版本。为了获得这些信息的回溯,必须启用调试符号。当使用cargo buildcargo run而不使用--release标志时,调试符号是默认启用的,就像我们这里的情况。

在上图的输出中,回溯的第6行指向我们项目中导致问题的那一行: src/main.rs的第920行。如果我们不想让我们的程序恐慌,我们应该从提到我们所写的文件的第一行所指向的位置开始调查。在本节第一个示例中,我们故意写了会恐慌的代码,解决恐慌的方法是不要请求超出向量索引范围的元素。当你的代码在未来出现恐慌时,你需要弄清楚代码用什么值做了什么动作来引起恐慌,以及代码应该做什么来代替。

我们将在本章后面的 "恐慌!或不恐慌!"一节中再来讨论painc!以及何时应该和不应该使用painc!来处理错误情况。

本章重点

  • 错误处理的概念
  • Rust语言中错误处理的painc!
  • Rust恐慌朔源以及如何使用

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

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

相关文章

Google结构化数据

为什么要向网页添加结构化数据&#xff1f; 添加结构化数据可让您获得对用户更有吸引力的搜索结果&#xff0c;并可能会鼓励用户与您的网站进行更多互动&#xff0c;这就是富媒体搜索结果。 以下是一些为网站实现了结构化数据的案例研究&#xff1a; Rotten Tomatoes 为 10 万…

【学习笔记之Linux】工具之gdb

背景知识&#xff1a; 首先我们要知道&#xff0c;程序的发布一共有两种模式&#xff0c;一种是debug模式&#xff0c;是我们程序员自己编写代码的模式&#xff0c;可以进行调试&#xff0c;这个模式下编译出来的程序是包含调试信息的&#xff1b;一种是release模式&#xff0c…

AntV G6 组织图使用(后端渲染数据)

一、业务场景&#xff1a; 点击按钮&#xff0c;跳转页面并显示该数据的组织架构图&#xff08;类似于粒子效果&#xff09; 二、问题描述&#xff1a; 初始写死的数据能显示&#xff0c;但是从接口请求到的数据赋上值 渲染不了 三、具体实现步骤&#xff1a; &#xff08;1&…

python GUI And Tkinter 01

目录 一、基础介绍 二、创建窗口 1、创建完窗口后还需要知道窗口的相关属性 2、widget相关控件 3、原本tkinter有的Widget。 4、widget的共同属性 1. Configuration 2. Event Processing 3. Event callbacks 4. Alarm handlersafter(time,callback)&#xff1a;间隔指定时间后调…

Python logging 库的『完整教程』

前言 本文的标题是『完整』。所谓『完整』&#xff0c;大意是想表达&#xff1a;提炼出一组最小的经验组合&#xff0c;并且能够快速应用于工程中&#xff0c;能 work&#xff0c;甚至能完美地 work。这篇文章就是想要做到『如何能完美地work』。 初衷 最原始的初衷就是&…

nmap 扫描数据分析

本案22端口为开放端口&#xff0c;110为未开放端口 Wireshark上使用下面的表达式 ip.addr192.168.104.127 and ip.addr192.168.104.61 and tcp.port22 ip.addr192.168.104.127 and ip.addr192.168.104.61 and tcp.port110 命令一、 nmap -sS SYN-->SYN ACK-->RST …

电脑技巧:Windows这些自带应用尽量不要删,否则影响系统运行

目录 第一种&#xff1a;带有“microsoft”字样的软件尽量不卸载。 第二种&#xff1a;带有“Intel”或者“英特尔”的程序名称不要卸载。 第三种&#xff1a;windows驱动程序包尽量不要卸载 第四种&#xff1a;Adobe flash player不建议卸载 当电脑太卡&#xff0c;运行变…

C/C++ - 从代码到可执行程序的过程

&#xff08;1&#xff09;预编译 主要处理源代码文件中的以“#”开头的预编译指令。处理规则见下&#xff1a; 删除所有的#define&#xff0c;展开所有的宏定义。处理所有的条件预编译指令&#xff0c;如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。处理“#inc…

简单工厂模式

简单工厂模式所谓组件&#xff1a;从设计上讲&#xff0c;组件就是能完成一定功能的封装体。小到一个类&#xff0c;大到一个系统&#xff0c;都可以称为组件&#xff0c;因为一个小系统放到更大的系统里面去&#xff0c;也就当个组件而已。模式定义&#xff1a;提供一个创建对…

servlet运用自定义分发优化servlet泛滥

servlet优化 Web 层的 Servlet 个数太多了&#xff0c;不利于管理和编写 我们发现每一个功能都需要定义一个 servlet&#xff0c;一个模块需要实现增删改查功能&#xff0c;就需要4个 servlet&#xff0c;模块一多就会造成servlet 泛滥。此时我们就想 servlet 能不能像 servi…

YOLOv6 训练自己的数据集

项目地址&#xff1a;https://github.com/meituan/YOLOv6 论文地址&#xff1a;https://arxiv.org/abs/2209.02976 论文解析&#xff1a;http://t.csdn.cn/0ZQbV YOLOv6 是一种专为工业应用设计的单级对象检测框架&#xff0c;具有硬件友好的高效设计和高性能。YOLOv6-N 在 NVI…

【windows】docker与docker-compose部署spring boot项目

看完不会用&#xff0c;我倒立**&#xff0c;保姆级教学 docker部署项目 采用Dockerfile部署 docker-compose部署项目 docker-compose部署&#xff0c;实际上是对容器的编排&#xff0c;以及容器间的一些依赖 比如一个springboot项目&#xff0c;需要使用redis&#xff0c;…

深入 Redis sds

文末有视频讲解 在上一个模块中&#xff0c;我和小伙伴们一起学习了 Redis 最核心的命令&#xff0c;主要涉及 String、List、Hash、Set、Sorted Set 五种数据结构的命令&#xff0c;同时&#xff0c;我们还介绍了每种数据结构的实战场景&#xff0c;并带领小伙伴们使用 Java 语…

11、ThingsBoard-租户配置

1、概述 租户配置(tenant profile)如其名是租户相关的配置,通俗一点就是给你这个租户的功能增加一些限制,如果你加钱,我就给你把限制设置高一点,thingsboard官方那个收费的版本不就是这样的吗?租户配置在系统层,系统管理员可以创建租户配置,然后使用租户配置为多个租…

centos7安装kubeadm

centos7安装kubeadm 一、基础设置 1、设置主机名 hostnamectl set-hostname master hostnamectl set-hostname node01vim /etc/hosts 192.168.198.169 master 192.168.198.170 note01hostnamectl hostnamectl 是在 centos7 中新增加的命令&#xff0c;它是是用来管理给定主机…

2023年我花费数小时整理的Java常用类进阶学习文档,你学会了吗?

文章目录1. 基本类型的包装类1.1 概念1.2 常用的属性1.3 常用的方法1.4 自动装箱和自动拆箱2. 字符串类2.1 String 类2.2 StringBuilder类2.3 StringBuffer类2.4 StringBuilder 的扩容机制3. 数字常用类3.1 Math 类3.2 Radom 类4. 枚举类5. 日期类5.1 Date 类5.2 DateFormat 类…

基于微信小程序的小区租拼车管理信息系统小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

学习理解10G Ethernet Subsystem之IEEE1588

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 学习理解10G Ethernet Subsystem之IEEE1588前言原理简介one step/two step1PPSIP 设置前言 1588大多是走在报文中&#xff0c;主要通过一些报文交互来完成同步过程&#xff…

在Docker中安装Gitea

目录在Docker中安装Gitea1、拉取最新Gitea官方镜像2、实例化一个Gitea容器3、Gitea需要数据源&#xff0c;因此使用mysql作为后端数据库4、在mysql中创建一个新数据库&#xff0c;起名gitea5、访问Gitea主页http://host:3000&#xff0c;进入初始配置页在Docker中安装Gitea 1、…

【自学Python】Python位运算符

Python位运算符 Python位运算符教程 在 Python 中&#xff0c;位运算符主要是用于 数值类型 的二进制的运算。 Python位运算符语法 位运算符说 明案例备注&按位与a & b返回 a 和 b 相与的结果|按位或a | b返回 a 和 b 相或的结果^按位异或a ^ b返回 a 和 b 相异或的…