【Rust自学】9.3. Result枚举与可恢复的错误 Pt.2:传播错误、?运算符与链式调用

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

9.3.1. 传播错误

当你编写的函数中包含了一些可能会执行失败的调用时,除了在函数里处理这个错误,还可以把错误返回给调用者,让它来决定如何进一步处理这个错误。

看个例子:

use std::fs::File;  
use std::io::{self, Read};  fn read_username_from_file() -> Result<String, io::Error> {  let f = File::open("6657.txt");  let mut f = match f {  Ok(file) => file,  Err(e) => return Err(e),  };  let mut s = String::new();  match f.read_to_string(&mut s) {  Ok(_) => Ok(s),  Err(e) => Err(e),  }  
}fn main() {  let result = read_username_from_file();  
}

这个代码的意图是从文件中读取用户名:

  • 它的返回类型是Result枚举,它的两个参数TE对应String类型和io::Error类型,也就是说,当一切顺利的时候,会返回Result下的Ok变体,Ok里包裹着String类型的用户名,如果遇到了问题,这个函数就会返回Result下的Err变体,在这个变体里会包含io::Error的实例。

  • 下面看函数体,首先使用File::open函数尝试打开一个文件,把Result类型赋给f,然后对f进行match操作(这里把第二个的f设为可变是因为下文的read_to_string会使用&mut self),如果操作成功会返回file把值赋给f,如果操作失败就会return Err(e),这里的e就是具体发生的错误,而在函数体里面遇到return关键字就表示函数的执行到此为止,返回return后面的参数,也就是Err(e)这个变体,错误类型恰好是io::Error,所以说返回值符合result的类型参数。

  • 如果File::open能操作成功的话,接下来函数就创建了一个可变的String,叫s,然后调用read_to_string方法把文件里的内容读取到变量s里面。当然read_to_string方法也可能会失败,所以后面还跟了一个match表达式。

  • 这个match表达式它的结尾没有分号,它也是这个函数的最后一个表达式,所以说它就是这个函数的返回结果。这个match有两个分支,如果这个操作能成功的话,就返回Result的Ok变体,并且把String类型的变量s封装到里面;如果操作失败,就返回Err变体,把错误e包裹在里面返回,而read_to_string方法的返回值类型恰好也是io::Error,所以返回值符合result的类型参数。

9.3.2. ?运算符

在Rust里传播错误的设计是非常常见的,所以Rust还专门提供了?这个运算符来简化传播错误的过程。

使用?实现上文例子的同样效果:

use std::fs::File;  
use std::io::{self, Read};  fn read_username_from_file() -> Result<String, io::Error> {  let mut f = File::open("6657.txt")?;  let mut s = String::new();  f.read_to_string(&mut s)?;  Ok(s)  
}  fn main() {  let result = read_username_from_file();  
}
  • 对于第一个?(第5行):File::open的返回类型是Result,然后加了?就是说如果File::open的返回值是Ok,那么包裹在Ok里的值就会作为表达式的结果返回赋给f,如果File::open的返回值是Err,那么就会终止函数的执行,把Err及里面包裹的错误信息作为整个函数的返回值返回(也就是return Err(e))。也就是说,第五行代码的效果等同于:
let f = File::open("6657.txt");  
let mut f = match f {  Ok(file) => file,  Err(e) => return Err(e),  
};  
  • 对于第二个?(第7行):如果read_to_string操作成功,它就会继续往下执行,成功的返回值实际上在代码中没有用到,而如果执行失败的话,那么就会终止函数的执行,把Err及里面包裹的错误信息作为整个函数的返回值返回(也就是return Err(e))。

  • 如果前面都操作成功,那么就写表达式Ok(s)String类型的s包裹在Ok变体里返回。

总结一下:把?用于Result,如果是Ok,那么Ok中的值就是表达式的结果,然后程序继续执行;如果操作失败,也就是Err,那么Err就是整个函数的返回值,就像使用了return

9.3.3. ?from函数

Rust提供了from函数,它来自std::connvert::From这个trait,而它的作用是在错误之间进行转换,将一个错误类型转化为另外一个错误类型,而被?所接收的错误,会隐式地被from函数处理,from会看当前代码所在的函数的返回值的错误类型是什么,然后转换为什么。

就以刚才的代码为例,read_username_from_file函数的返回值是Result<String, io::Error>from函数就看得出来函数需要io::Error作为发生错误时的返回值,就会把不同的错误类型转化为io::Error,这里只是碰巧所有的函数体内的错误类型都是io::Error,就不需要转化这一步。

这个特点用于针对不同的错误原因,返回同一种错误类型的情况非常有用。但前提条件是涉及到的错误类型实现了转换为所返回的错误类型的from函数就可以。

9.3.4. 链式调用

其实之前的例子还可以继续优化,就是使用链式调用的形式。优化后的代码如下:

use std::fs::File;  
use std::io::{self, Read};  fn read_username_from_file() -> Result<String, io::Error> {  let mut s = String::new();  File::open("6657.txt")?.read_to_string(&mut s)?;  Ok(s)  
}  fn main() {  let result = read_username_from_file();  
}

刚刚说过了,把?用于Result,如果是Ok,那么Ok中的值就是表达式的结果,然后程序继续执行。那就可以消除原代码中赋值的步骤,直接使用链式调用来执行。

9.3.5. ?只能用于返回Result类型的函数

看个例子:

use std::fs::File;  
fn main() {  let result = File::open("6657.txt")?;  
}

输出:

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)--> src/main.rs:3:40|
2 | fn main() {| --------- this function should return `Result` or `Option` to accept `?`
3 |     let result = File::open("6657.txt")?;|                                        ^ cannot use the `?` operator in a function that returns `()`|= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
help: consider adding return type|
2 ~ fn main() -> Result<(), Box<dyn std::error::Error>> {
3 |     let result = File::open("6657.txt")?;
4 +     Ok(())|

报错内容是?运算符只能用于返回值是Result或者Option这类实现了Try这个trait的类型,而main函数的返回类型是(),也就是单元类型,相当于什么也没返回。

但是,谁说main函数的返回类型一定是单元类型呢?只要把它的返回值改成Result类型不就完了吗?代码如下:

use std::error::Error;  
use std::fs::File;  fn main() -> Result<(), Box<dyn Error>> {  let result = File::open("6657.txt")?;  Ok(())  
}
  • 把返回类型改为Result<(), Box<dyn Error>>,也就是说如果程序正常运行,会返回Ok这个变体,里面呢包裹着单元类型;如果没有正常运行,会返回Err这个变体,包裹着Box<dyn Error>(其中的Errorstd::error::Error),这是一个trait对象,在以后会讲,这里可以把它简单地理解为任何可能的错误类型。

  • 如果能成功读取,那么?就会把包裹在Ok里的文件数据返回赋给result,然后继续执行,Ok(())main函数里的最后一个表达式,它返回了Ok这个变体,同时把单元类型包裹着。

  • 如果不能成功读取,那么?就会把Err(e)作为main函数的返回值返回回去,并且函数执行到此结束。

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

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

相关文章

鸿蒙HarmonyOS开发:拨打电话、短信服务、网络搜索、蜂窝数据、SIM卡管理、observer订阅管理

文章目录 一、call模块&#xff08;拨打电话&#xff09;1、使用makeCall拨打电话2、获取当前通话状态3、判断是否存在通话4、检查当前设备是否具备语音通话能力 二、sms模块&#xff08;短信服务&#xff09;1、创建短信2、发送短信 三、radio模块&#xff08;网络搜索&#x…

TVS二极管选型【EMC】

TVS器件并联在电路中&#xff0c;当电路正常工作时&#xff0c;他处于截止状态&#xff08;高阻态&#xff09;&#xff0c;不影响线路正常工作&#xff0c;当线路处于异常过压并达到其击穿电压时&#xff0c;他迅速由高阻态变为低阻态&#xff0c;给瞬间电流提供一个低阻抗导通…

sqlalchemy-access库操作MS Access

因目前项目中数据处理的量稍大&#xff0c;为了方便和业务进行交互&#xff0c;对数据的加工和处理放到微软桌面数据库MS Access中。然后有些地方通过 Python 来操作 MS Access 数据库&#xff0c;用到 sqlalchemy-access库。本文对操作的要点做简单的描述。 之前写过一篇 Pyt…

UnityRenderStreaming使用记录(三)

测试UnityRenderStreaming在Ubuntu24.04.1LTS上的表现 先放上运行图操作系统 Ubuntu24.04.1LTSUnity测试工程环境相关修改遇到的问题 先放上运行图 操作系统 Ubuntu24.04.1LTS 系统下载地址 https://cn.ubuntu.com/download/desktop安装UnityHub https://blog.csdn.net/AWNUXC…

==和===的区别,被坑的一天

在 JavaScript 中&#xff0c; 和 都用于比较两个值&#xff0c;但它们有一个重要的区别&#xff1a; 1. (宽松相等运算符) 进行比较时&#xff0c;会 自动类型转换&#xff08;也叫做强制类型转换&#xff09;&#xff0c;即如果比较的两个值的类型不同&#xff0c;JavaScr…

Git的使用流程(详细教程)

目录 01.Git是什么&#xff1f; 1.1 Git简介 1.2 SVN与Git的最主要的区别 1.3 GIt主要特点 02.Git是干什么的&#xff1f; 2.1.Git概念汇总 2.2 工作区/暂存区/仓库 2.3 Git使用流程 03.Git的安装配置 3.1 Git的配置文件 3.2 配置-初始化用户 3.3 Git可视化…

使用Docker部署最新版JupyterHub

拉取镜像 docker pull jupyterhub/jupyterhub:latest启动镜像 docker run -d -p 8000:8000 --name jupyterhub jupyterhub/jupyterhub:latest jupyterhub进入容器 docker exec -it jupyterhub bash生成jupyterhub的配置文件 jupyterhub --generate-config# 有需要可以安装中…

liunx下载gitlab

1.地址&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/ 安装 postfix 并启动 yum install postfix systemctl start postfix systemctl enable postfix ssh服务启动 systemctl enable sshd systemctl start sshd开放 ssh 以及 http 服务&#xff0c…

【AI大模型】深入GPT-2模型细节:揭秘其卓越性能的秘密

目录 &#x1f354; GPT2的架构 &#x1f354; GPT2模型的细节 2.1 模型过程 2.2 GPT2工作细节探究 &#x1f354; 小结 学习目标 掌握GPT2的架构掌握GPT2的训练任务和模型细节 &#x1f354; GPT2的架构 从模型架构上看, GPT2并没有特别新颖的架构, 它和只带有解码器模块…

oceanbase集群访问异常问题处理

1.报错现象 2.问题排查 检查obproxy状态发现为不可用状态 重启obproxy 依次重启Obproxy集群 观察任务状态 重启完成 Obproxy状态正常 3.验证登录 登录成功

Echarts+vue电商平台数据可视化——webSocket改造项目

websocket的基本使用&#xff0c;用于测试前端能否正常获取到后台数据 后台代码编写&#xff1a; const path require("path"); const fileUtils require("../utils/file_utils"); const WebSocket require("ws"); // 创建WebSocket服务端的…

【数据结构】双向循环链表的使用

双向循环链表的使用 1.双向循环链表节点设计2.初始化双向循环链表-->定义结构体变量 创建头节点&#xff08;1&#xff09;示例代码&#xff1a;&#xff08;2&#xff09;图示 3.双向循环链表节点头插&#xff08;1&#xff09;示例代码&#xff1a;&#xff08;2&#xff…

【深度学习】卷积网络代码实战ResNet

ResNet (Residual Network) 是由微软研究院的何凯明等人在2015年提出的一种深度卷积神经网络结构。ResNet的设计目标是解决深层网络训练中的梯度消失和梯度爆炸问题&#xff0c;进一步提高网络的表现。下面是一个ResNet模型实现&#xff0c;使用PyTorch框架来展示如何实现基本的…

【51项目】51单片机自制小霸王游戏机

视频演示效果&#xff1a; 纳新作品——小霸王游戏机 目录&#xff1a; 目录 视频演示效果&#xff1a; 目录&#xff1a; 前言&#xff1a; 一、连接方式&#xff1a; 1.1 控制引脚 1.2. 显示模块 1.3. 定时器 1.4. 游戏逻辑与硬件结合 1.5. 中断处理 二、源码分析&#xff1a…

2024/12/29 黄冈师范学院计算机学院网络工程《路由期末复习作业一》

一、选择题 1.某公司为其一些远程小站点预留了网段 172.29.100.0/26&#xff0c;每一个站点有10个IP设备接到网络&#xff0c;下面那个VLSM掩码能够为该需求提供最小数量的主机数目 &#xff08; &#xff09; A./27 B./28 C./29 D./30 -首先审题我们需要搞清楚站点与网…

【OpenCV】使用Python和OpenCV实现火焰检测

1、 项目源码和结构&#xff08;转&#xff09; https://github.com/mushfiq1998/fire-detection-python-opencv 2、 运行环境 # 安装playsound&#xff1a;用于播放报警声音 pip install playsound # 安装opencv-python&#xff1a;cv2用于图像和视频处理&#xff0c;特别是…

Vue2: table加载树形数据的踩坑记录

table中需要加载树形数据,如图: 官网给了两个例子,且每个例子中的tree-props都是这么写的: :tree-props="{children: children, hasChildren: hasChildren}" 给我一种错觉,以为数据结构中要同时指定children和hasChildren字段,然而,在非懒加载模式下,数据结…

深度学习模型预测值集中在某一个值

深度学习模型&#xff0c;训练过程中&#xff0c;经常遇到预测的结果集中在某个值&#xff0c;而且在学习的过程中会变&#xff0c;样例如下。 主要有如下解决方案 1、更换relu ->tanh 或者其他激活函数 2、更改随机种子&#xff0c;估计是没有初始化好&#xff0c;或者调…

图书项目:整合SSM

步骤&#xff1a; pom文件&#xff1a;导包&#xff0c;写入静态资源导出配置&#xff0c;连接数据库 建包&#xff1a;controller dao/mapper pojo service 配置文件&#xff1a;mybatis-config.xml applicationContext.xml&#xff08;Spring的配置文件&#xff09; datab…

javacript中function (res) {}与箭头函数表达式(res) =>{}的区别

javacript中function (res) {}与(res) &#xff1e;{}的区别 function (res) {} 代码演示 let shape {name:长方形,say:function(){console.log(我是this.name)setTimeout(function(){console.log(3秒后输出我是: this.name); //this.name为undefined}, 3000)} }shape.sa…