Rust:深入浅出说一说 Error 类型

1. Rust 的错误返回机制

Rust 函数计算过程如果发生错误怎么办?Rust没有采取 C++ 的异常机制,而是允许直接返回错误信息。

这意味着,Rust 提供了错误返回机制,允许函数正常结束时返回计算结果,同时,如果计算过程中出现错误,也可以返回结果。这就是系统库提供的 Result 数据类型。如下面的示意函数:

fn my_function() -> Result<i32, MyError> {// 返回正常结果return Ok(123);// 或返回错误return Err(MyError::new(/*可能有参数*/));
}

2. Rust 的 Error 特性

这个机制中,比较难以理解的是 MyError 类型如何设计实现。实际上,Rust 要求用户自定义错误类型实现 std::error::Error 这个特性即可。 std::error::Error 在 Rust 的不同版本中曾经出现过多种定义,在目前的成熟版本中已经大道至简了。它的定义大致如下:

pub trait Error: Debug + Display {fn source(&self) -> Option<&(dyn Error + 'static)> {None}
}

3. Error 数据类型的最小实现

Error 特性就需要实现一个函数,而且已经有了默认实现。也就是说,最简单的错误类型实现可能只需要下面的代码即可:

#[derive(Debug)]
struct MyError {}impl Display for MyError {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {write!(f, "error here!")}
}impl Error for MyError {}

impl Error for MyError {} 这行代码有用吗?答案是有用。它可以让 MyError 实现 source(&self) 函数。

4. Error 数据类型如何附加错误信息?

想给 Error 数据类型发加上错误信息怎么办?

很简单,添加一个错误信息属性即可。示例代码如下:

#[derive(Debug)]
struct MyError {message: String;
}impl MyError {fn new(message: &str) -> Self {MyError{message: message.to_string(),}}
}impl Display for MyError {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {write!(f, message)}
}impl Error for MyError {}

5. 为什么 Rust 不提供一个通用的 Error 数据类型,让一些简单的程序不再编写自己的专用 Error 错误类型?

Rust 不直接提供一个通用的 Error 数据类型,而是采用了更为灵活和强大的错误处理机制,主要基于 trait(接口)的方式来实现错误处理,这是 Rust 设计中的一个核心原则:零成本抽象(Zero-cost abstractions)和不允许隐式转换。以下是一些主要原因:

  1. 类型安全:Rust 强调类型安全,每个错误都可能有其独特的上下文和属性。提供一个通用的 Error 类型会失去这种类型安全性,因为所有的错误都会被当作同一类型处理,从而丢失了关于错误本质的具体信息。通过使用特定的错误类型,Rust 能够提供更准确的错误信息,这对于调试和错误处理非常重要。

  2. 灵活性:通过自定义错误类型,开发者可以根据需要为错误添加任意数量的字段和方法。这些字段和方法可以提供有关错误的额外信息(如错误代码、消息、堆栈跟踪等),从而提高了错误处理的灵活性和表达力。

  3. 错误链(Error Chaining):Rust 通过 std::error::Error trait 和 std::fmt::Display trait 提供了错误链的功能。虽然这要求你定义自己的错误类型,但它允许你将多个错误连接成一个链,并在处理时逐一访问。这种机制在复杂系统中特别有用,因为它可以保持错误的上下文并允许进行更细致的错误分析。

  4. 零成本抽象:Rust 的设计哲学之一是“零成本抽象”,即使用高级语言特性(如泛型、trait 等)而不增加运行时开销。提供一个通用的 Error 类型可能会引入隐式转换和额外的运行时开销,这与 Rust 的设计原则相悖。

  5. 可组合性:Rust 的错误处理系统是可组合的,意味着你可以轻松地将多个错误处理逻辑组合在一起。虽然这要求你定义自己的错误类型,但它提供了更大的灵活性和可重用性。例如,你可以创建一个通用的错误包装器(wrapper),用于包装不同类型的错误并添加额外的上下文。

  6. 文档和可读性:自定义错误类型有助于提高代码的可读性和可维护性。当你看到一个具体的错误类型时,你可以很容易地知道它代表什么类型的错误,而不需要查看该错误的文档或源代码。此外,自定义错误类型还可以包含有用的文档字符串,这些字符串提供了关于错误的额外信息。

尽管 Rust 不提供一个通用的 Error 类型,但它通过提供 std::error::Error trait 和相关机制来支持灵活且强大的错误处理。这些机制鼓励开发者编写类型安全、灵活且易于维护的代码。

6. 如何定义一个“完整的” Error 类型

下面给出标准库的一段示意性代码。

我们可以注意到,错误代码依赖 ErrorKind 枚举类型。定义自己的错误类型枚举,是自定义 Error 类型的关键。正因为有了这个枚举类型,收到错误的一方才能快速准确确定错误类型。换言之,宁愿不要 message 属性,也建议提供错误类型属性。

在 Rust 标准库中,std::io::Error 是一个用于表示 I/O 操作错误的类型。这个类型是由 Rust 标准库提供的,而不是由用户直接定义的。不过,我们可以根据 Rust 的错误处理机制和类型系统的特点,给出一个示意性的表示,以帮助你理解 std::io::Error 是如何被设计的。

请注意,实际的 std::io::Error 实现可能包含更多的细节和复杂性,包括与平台相关的错误代码、内部状态管理等。但以下是一个简化的示意性代码,用于说明 std::io::Error 可能的基本结构和一些关键特性:

// 假设的模块和类型定义,仅用于示意
mod io {// 定义一个枚举来表示不同类型的 I/O 错误#[non_exhaustive] // 标记枚举可能在未来版本中增加新的变体pub enum ErrorKind {NotFound,PermissionDenied,ConnectionRefused,// ... 其他可能的错误类型}// Error 是一个结构体,用于封装错误的具体信息#[derive(Debug, PartialEq)]pub struct Error {// 错误类型kind: ErrorKind,// 可能包含额外的错误信息或上下文message: String,// ... 可能还有其他字段,如错误码、源位置等}// 实现 std::error::Error trait,以便 Error 可以被用作错误类型impl std::error::Error for Error {fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {// 如果 Error 封装了另一个错误,这里可以返回它// 在这个简化的例子中,我们假设没有封装其他错误None}}// 实现 std::fmt::Display trait,以便可以格式化打印错误信息impl std::fmt::Display for Error {fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {write!(f, "{}", self.message) // 简化处理,只打印消息}}// 可能还有其他方法和函数,如从 raw OS 错误码创建 Error 实例等// ...
}// 注意:上述代码是示意性的,并不是 std::io::Error 的实际实现
// 在真实的 Rust 标准库中,std::io::Error 会更复杂,并且会利用 Rust 的高级特性来提供更强大的功能

在 Rust 的真实 std::io 模块中,Error 类型实际上是一个更复杂的结构体或枚举,它可能包含与平台相关的错误码、错误消息的本地化支持、以及可能链接到源错误的 Source 链等。此外,std::io::Error 还实现了 std::error::Errorstd::fmt::Display trait,以及可能的其他 trait,如 std::fmt::Debug,以支持错误处理和调试。

由于 Rust 标准库的实现可能会随着版本更新而变化,因此建议查看最新的 Rust 文档或标准库源代码以获取准确的信息。

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

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

相关文章

纯小白安装pytorch(快速上手)

1.首先进入你的虚拟环境(不进入也没关系) 在anaconda prompt中打开&#xff0c;输入activate 虚拟环境2.查看自己的conda源 conda config --show channels3.清空自己的conda源 conda config --remove-key channels4.添加源 conda config --add channels https://mirrors.tu…

蓝桥杯-STM32G431RBT6(解决LCD与LED引脚冲突的问题)

一、LCD与LED为什么会引脚冲突 LCD与LED引脚共用。 网上文章是在LCD_WriteRAM、LCD_WriteRAM_Prepare、LCD_WriteReg中添加&#xff0c;但问题并没有解决。 二、使用步骤 在如下函数中加入uint16_t tempGPIOC->ODR; GPIOC->ODRtemp; LCD_Init(); void LCD_C…

动态规划:07.路径问题_珠宝的最大价值_C++

题目链接&#xff1a;LCR 166. 珠宝的最高价值 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/description/ 一、题目解析 题目&#xff1a; 解析&#xff1a; 有过做前几道题的经验&#xff0c;我们会发现这道题其实就…

IDEA甚至前进后退跳转键

1&#xff1a;快捷键设置 idea设置前进、后退快捷键_idea后退前进快捷键-CSDN博客 2&#xff1a;界面设置 IDEA添加“前进 后退 ”添加到工具栏_idea 最新社区版 左右箭头怎么设置-CSDN博客

安卓显示驱动

安卓显示驱动是用于在Android设备上提供图形和视频显示的底层软件组件。 显示驱动在Android系统中扮演着至关重要的角色&#xff0c;它们负责将图形和视频内容从系统内存传输到显示屏上。这些驱动程序确保了用户界面、图像、视频和游戏等视觉元素的正常显示。以下是关于安卓显…

SpringBoot2:web开发常用功能实现及原理解析-上传与下载

文章目录 一、上传文件1、前端上传文件给Java接口2、Java接口上传文件给Java接口 二、下载文件1、前端调用Java接口下载文件2、Java接口下载网络文件到本地3、前端调用Java接口下载网络文件 一、上传文件 1、前端上传文件给Java接口 Controller接口 此接口支持上传单个文件和…

如何准备教师资格证科目三“学科知识与教学能力”的考试与面试?(理科导向:数学/物理)

如何准备教师资格证科目三“学科知识与教学能力”的考试与面试&#xff1f;&#xff08;理科导向&#xff1a;数学/物理&#xff09; ​ 目录 收起 1 前言 1.1 自身经历 1.2 教师资格证的作用 2 知识点题型分数的分布与学习建议 2.1 科目三的知识点分数分布&#xff1a; …

MessagesPlaceholder

MessagesPlaceholder 在LangChain框架中是一个非常重要的概念&#xff0c;它主要用于在构建自然语言处理&#xff08;NLP&#xff09;应用时&#xff0c;实现消息模板的动态插入和格式化。以下是对MessagesPlaceholder的详细解释&#xff1a; 一、定义与作用 MessagesPlacehol…

求和(2)

题目描述 输入两个正整数 l,r&#xff0c;编程计算 l(l1)(l2)...(r−1)r 的结果并输出。 输入格式 一行两个整数 l 和 r 输出格式 一个整数&#xff0c;根据题意计算后的结果 样例数据 样例输入#1 1 5样例输出#1 15样例输入#2 8 10样例输出#2 27数据范围 对于100%的…

Pycharm中虚拟环境依赖路径修改

引言 在pycharm中创建完虚拟环境后&#xff0c;它会自动将同文件夹底下的site_pakages等子文件夹作为该虚拟环境的依赖项。我们可以通过sys.path来查看当前虚拟环境的依赖路径&#xff0c;在这些依赖路径底下的包就可以被import到。但有些情况下&#xff0c;在我们创建了一个虚…

【Ubuntu】虚拟机安装USB摄像头ROS驱动 usb_cam(最新方法)

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

Nest.js

Nestjs中文文档链接 TypeORM 中文文档 小满视频 1. 安装Nest.js 安装脚手架 npm i -g nestjs/cli创建nestjs工程 nest new工程目录 app.module.ts 根模块用于处理其他类的引用与共享。app.controller.ts 常见功能是用来处理http请求&#xff08;处理请求的路径&#xff09…

如何升级用 Helm 安装的极狐GitLab Runner?

本分分享如何对 Helm 安装的 Runner 进行升级。整个过程分为三步&#xff1a;1、确定 Runner 最新版本或者想要升级的版本是否存在&#xff1b;2、用 Helm upgrade 命令进行升级&#xff1b;3、升级确认。 极狐GitLab 为 GitLab 的中国发行版&#xff0c;中文版本对中国用户更…

react18基础教程系列-- 框架基础理论知识mvc/jsx/createRoot

react的设计模式 React 是 mvc 体系&#xff0c;vue 是 mvvm 体系 mvc: model(数据)-view(视图)-controller(控制器) 我们需要按照专业的语法去构建 app 页面&#xff0c;react 使用的是 jsx 语法构建数据层&#xff0c;需要动态处理的的数据都要数据层支持控制层: 当我们需要…

1730. 购买贺年卡

代码 #include<bits/stdc.h> using namespace std; struct c {int a,b; }t[1005]; int cmp(c a,c b) {return a.a>b.a; } int main() {int n,m,sum0;cin>>n>>m;for(int i1;i<m;i){cin>>t[i].a>>t[i].b;}sort(t1,t1m,cmp);for(int im;i&g…

如何在Linux下升级R版本和RStudio

一、升级R版本 在Linux上&#xff0c;R的安装通常通过包管理器完成。不同的Linux发行版&#xff08;如Ubuntu、Debian、Fedora等&#xff09;可能略有不同。下面以Ubuntu为例&#xff0c;介绍如何升级R版本。如果你使用其他发行版&#xff0c;步骤可能类似。 二.更新步骤 2.…

【可视化大屏系列】数据列表自动滚动效果

要实现列表的自动滚动效果&#xff0c;这里提供两种解决方案&#xff1a; 1.vue插件 官方文档&#xff1a;链接: vue-seamless-scroll &#xff08;1&#xff09;安装依赖 npm install vue-seamless-scroll --save&#xff08;2&#xff09;全局注册&#xff08;main.js中&a…

H5依赖安装

依赖安装 git和sourceTree编辑器使用vscode下载nvm 和nodejs git和sourceTree 使用 ssh-keygen -t rsa 进行密钥获取 git下载地址&#xff1a;https://git-scm.com/ sourceTree下载地址&#xff1a;https://www.sourcetreeapp.com/ 编辑器使用vscode 最新版网址&#xff1a;…

【机器学习】--- 自监督学习

1. 引言 机器学习近年来的发展迅猛&#xff0c;许多领域都在不断产生新的突破。在监督学习和无监督学习之外&#xff0c;自监督学习&#xff08;Self-Supervised Learning, SSL&#xff09;作为一种新兴的学习范式&#xff0c;逐渐成为机器学习研究的热门话题之一。自监督学习…

【linux-Day3】linux的基本指令<中>

【linux-Day3】linux的基本指令<中> linux下的基本指令&#x1f4e2;man&#xff1a;访问linux手册页&#x1f4e2;echo&#xff1a;把字符串写入指定文件中&#x1f4e2;cat&#xff1a;查看目标文件的内容&#x1f4e2;cp&#xff1a;复制文件或目录&#x1f4e2;mv&am…