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

news2025/1/6 19:52:16

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

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.coloradmin.cn/o/2271090.html

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

相关文章

Win32汇编学习笔记03.RadAsm和补丁

Win32汇编学习笔记03.RadAsm和补丁-C/C基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net 扫雷游戏啊下补丁 在扫雷游戏中,点关闭弹出一个确认框,确认之后再关闭,取消就不关闭 首先第一步就是确认关闭按钮响应的位置,一般都是 WM_CLOSE 的消息 ,消息响应一般都在过…

OSPF特殊区域(open shortest path first LSA Type7)

一、区域介绍 1、Stub区域 Stub区域是一种可选的配置属性。通常来说&#xff0c;Stub区域位于自治系统的边界&#xff0c;例如&#xff0c;只有一 个ABR的非骨干区域。在这些区域中&#xff0c;设备的路由表规模以及路由信息传递的数量都会大量减少。 kill 4 5类type 传递1 …

论文解读之Generative Dense Retrieval: Memory Can Be a Burden

本次论文解读&#xff0c;博主带来生成式稠密检索&#xff1a;记忆可能成为一种负担的论文分享 一、简介 生成式检索根据给定的查询&#xff0c;自回归地检索相关的文档标识符&#xff0c;在小规模的文档库中表现不错&#xff0c;通过使用模型参数记忆文档库&#xff0c;生成…

vue,使用unplugin-auto-import避免反复import,按需自动引入

项目库&#xff1a;https://github.com/unplugin/unplugin-auto-import 参考&#xff1a; https://juejin.cn/post/7012446423367024676 https://cloud.tencent.com/developer/article/2236166 背景&#xff1a; vue3项目中&#xff0c;基本所有页面都会引入vue3框架的api&…

[深度学习] 大模型学习1-大语言模型基础知识

大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;是一类基于Transformer架构的深度学习模型&#xff0c;主要用于处理与自然语言相关的各种任务。简单来说&#xff0c;当用户输入文本时&#xff0c;模型会生成相应的回复或结果。它能够完成许多任务&…

OCR图片中文字识别(Tess4j)

文章目录 Tess4J下载 tessdataJava 使用Tess4j 的 demo Tess4J Tess4J 是 Tesseract OCR 引擎的 Java 封装库&#xff0c;它让 Java 项目更轻松地实现 OCR&#xff08;光学字符识别&#xff09;功能。 下载 tessdata 下载地址&#xff1a;https://github.com/tesseract-ocr/…

Vue2/Vue3使用DataV

Vue2 注意vue2与3安装DataV命令命令是不同的Vue3 DataV - Vue3 官网地址 注意vue2与3安装DataV命令命令是不同的 vue3vite 与 Vue3webpack 对应安装也不同vue3vite npm install kjgl77/datav-vue3全局引入 // main.ts中全局引入 import { createApp } from vue import Da…

【JVM】总结篇-字节码篇

字节码篇 Java虚拟机的生命周期 JVM的组成 Java虚拟机的体系结构 什么是Java虚拟机 虚拟机&#xff1a;指以软件的方式模拟具有完整硬件系统功能、运行在一个完全隔离环境中的完整计算机系统 &#xff0c;是物理机的软件实现。常用的虚拟机有VMWare&#xff0c;Visual Box&…

国内Ubuntu环境Docker部署Stable Diffusion入坑记录

国内Ubuntu环境Docker部署Stable Diffusion入坑记录 本文旨在记录使用dockerpython进行部署 stable-diffusion-webui 项目时遇到的一些问题&#xff0c;以及解决方案&#xff0c;原项目地址: https://github.com/AUTOMATIC1111/stable-diffusion-webui 问题一览&#xff1a; …

音频进阶学习九——离散时间傅里叶变换DTFT

文章目录 前言一、DTFT的解释1.DTFT公式2.DTFT右边释义1&#xff09; 复指数 e − j ω n e^{-j\omega n} e−jωn2&#xff09;序列与复指数相乘 x [ n ] ∗ e − j ω n x[n]*e^{-j\omega n} x[n]∗e−jωn复指数序列复数的共轭正交正交集 3&#xff09;复指数序列求和 3.DTF…

【Leecode】Leecode刷题之路第99天之恢复二叉搜索树

题目出处 99-恢复二叉搜索树-题目出处 题目描述 个人解法 思路&#xff1a; todo代码示例&#xff1a;&#xff08;Java&#xff09; todo复杂度分析 todo官方解法 99-恢复二叉搜索树-官方解法 方法1&#xff1a;显式中序遍历 思路&#xff1a; 代码示例&#xff1a;&…

利用AI优化SEO提升关键词排名的有效策略

内容概要 随着数字化时代的到来&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;在各类企业的在线营销战略中占据了越来越重要的位置。而人工智能&#xff08;AI&#xff09;技术的迅速发展为SEO带来了新的机遇和挑战。通过智能化的数据分析和智能内容生成&#xff0c;企…

YOLO分割数据集转化(json转TXT)

一、数据集转化 import json import os from tqdm import tqdm import glob import os.path as ospdef json_to_txt(jsonfilePath, resultDirPath):"""jsonfilePath: labelme标注好的*.json文件所在文件夹resultDirPath: 转换好后的*.txt保存文件夹""…

中建海龙:科技助力福城南产业片区绿色建筑发展

在快速发展的城市化进程中&#xff0c;绿色建筑以其环保、节能、可持续的特点日益受到重视。作为建筑工业化领域的领军企业&#xff0c;中建海龙科技有限公司&#xff08;简称“中建海龙”&#xff09;凭借其卓越的科技实力和创新举措&#xff0c;在推动绿色建筑发展方面做出了…

基于深度学习算法的AI图像视觉检测

基于人工智能和深度学习方法的现代计算机视觉技术在过去10年里取得了显著进展。如今&#xff0c;它被广泛用于图像分类、人脸识别、图像中物体的识别等。那么什么是深度学习&#xff1f;深度学习是如何应用在视觉检测上的呢&#xff1f; 什么是深度学习&#xff1f; 深度学习是…

大数据技术-Hadoop(四)Yarn的介绍与使用

目录 一、Yarn 基本结构 1、Yarn基本结构 2、Yarn的工作机制 二、Yarn常用的命令 三、调度器 1、Capacity Scheduler&#xff08;容量调度器&#xff09; 1.1、特点 1.2、配置 1.2.1、yarn-site.xml 1.2.2、capacity-scheduler.xml 1.3、重启yarn、刷新队列 测试 向hi…

python修改ppt中的文字部分及插入图片

批量修改ppt中的某个模块&#xff0c;或者批量制作奖状等场景会用到&#xff1b; import os import pandas as pd from pptx import Presentation from pptx.util import Inchesfilepath/Users/kangyongqing/Documents/kangyq/202303/分析模版/批量制作/file1时段预警_副本.pp…

数据库新建用户后(Host:%),报错:localhost无法连接

存在问题 在给数据库&#xff08;MySQL、MariaDB等&#xff09;创建了新的用户名&#xff08;eg&#xff1a;maxscale&#xff09;后&#xff0c;无法使用新用户名登录&#xff0c;并报如下错误&#xff1a;ERROR 1045 (28000): Access denied for user maxscalelocalhost (us…

《机器学习》——逻辑回归(下采样)

文章目录 什么是下采样&#xff1f;为什么在逻辑回归中要使用下采样&#xff1f;使用下采样和不使用下采样的区别实例1、实例内容2、实例步骤 什么是下采样&#xff1f; 下采样&#xff08;Down - Sampling&#xff09;是一种数据处理技术&#xff0c;主要用于处理数据集中不同…

ACM算法模板

ACM算法模板 起手式基础算法前缀和与差分二分查找三分查找求极值分治法&#xff1a;归并排序 动态规划基本线性 d p dp dp最长上升子序列I O ( n 2 ) O(n ^ 2) O(n2)最长上升子序列II O ( n l o g n ) O(nlogn) O(nlogn) 贪心二分最长公共子序列 背包背包求组合种类背包求排列…