【Rust自学】12.4. 重构 Pt.2:错误处理

12.4.0. 写在正文之前

第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print),是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。

这个项目分为这么几步:

  • 接收命令行参数
  • 读取文件
  • 重构:改进模块和错误处理(本文)
  • 使用TDD(测试驱动开发)开发库功能
  • 使用环境变量
  • 将错误信息写入标准错误而不是标准输出

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

12.4.1. 回顾

上一节中为了模块化我们为变量创建了结构体,还把读取指令的函数独立出去改成了结构体的方法。以下是截止到上一篇文章所写出的所有代码:

use std::env;  
use std::fs;  struct Config {  query: String,  filename: String,  
}  fn main() {  let args:Vec<String> = env::args().collect();  let config = Config::new(&args);  let contents = fs::read_to_string(config.filename)  .expect("Somthing went wrong while reading the file");  println!("With text:\n{}", contents);  
}  impl Config {  fn new(args: &[String]) -> Config {  let query = args[1].clone();  let filename = args[2].clone();  Config {  query,  filename,  }  }  
}

12.4.2. 意料之外的输入

这个程序能正确运行的前提是用户输入的输入无误,那我们试试不带参数的输入会引发什么:

$ cargo runCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0sRunning `target/debug/minigrep`
thread 'main' panicked at src/main.rs:27:21:
index out of bounds: the len is 1 but the index is 1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

它提示"Index out of bound"索引越界,作为程序编写者的我们明白这是因为参数不够导师程序在使用索引获取参数时越界触发恐慌。但是作为用户就不可能看懂这个报错信息,无法纠正错误。

这一篇文章要做的就是让程序产生的错误信息易于理解。

12.4.3. 指定报错信息

让用户理解报错信息的方式就是自己指定一个报错信息。刚刚的例子是在运行Config::new时索引越界,所以我们就修改这个地方:

impl Config {  fn new(args: &[String]) -> Config {  if args.len() < 3 {  panic!("Not enough arguments");  }  let query = args[1].clone();  let filename = args[2].clone();  Config {  query,  filename,  }  }  
}

如果args的元素数量小于三就发生恐慌打印"Not enough arguments"来提示用户输入的参数太少了。

再试试不带参数的输入:

$ cargo runCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0sRunning `target/debug/minigrep`
thread 'main' panicked at src/main.rs:26:13:
not enough arguments
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

这一次的错误信息比上一次就好很多了。

但是它仍然残留了一些其他的信息,比如"thread ‘main’ panicked at src/main.rs:26:13:“和"note: run with RUST_BACKTRACE=1 environment variable to display a backtrace”,这些内容是给程序员看的不是给用户看的。所以这些信息也得去掉。

12.4.4. 使用Result类型

panic!适用于程序本身出现问题时的恐慌,而这里却少参数的输入是程序使用时的问题,针对这种问题,使用Result类型来传播错误(这部分的内容详见 9.2. Result枚举与可恢复的错误 Pt.1 & Pt.2)才是最优解:

impl Config {  fn new(args: &[String]) -> Result<Config, &'static str> {  if args.len() < 3 {  return Err("Not enough arguments");  }  let query = args[1].clone();  let filename = args[2].clone();  Ok(Config { query, filename})  }  
}
  • 报错的信息需要用Err包裹,成功的返回值需要用Ok包裹
  • Result类型的Ok返回Config实例,Err返回&str字符串字面值,但是编译器不知道这个&str是从哪里来的以及它的生命周期有多长,所以得带生命周期,我们需要它在程序运行时始终保持有效,写成&'static str这个静态生命周期。

new函数的返回值都变了,main函数里接收值的逻辑也得变:

let config = Config::new(&args).unwrap_or_else(|err| {  println!("Problem parsing arguments: {}", err);  process::exit(1);  
});

unwrap_or_else这个方法会接收Result类型,如果是Ok,就会把Ok附带的值直接返回赋给变量,类似于unwrap;如果是Err,那么这个方法会调用一个闭包(closure)。

闭包是我们定义的匿名函数,并将其作为参数传递给unwrap_or_else。其写法是两个管道符||,在中间放变量名,相当于一个参数,这里就放了err,这个err可以在闭包的函数体内被调用,比如在打印错误时就使用了err

然后使用标准库的process::exit这个函数,使用前记得先导入一下:use std::process;,如果调用exit函数,程序的执行就会立即终止,而其参数,也就是示例代码中的1就作为程序退出时的状态码,这样显示到println!("Problem parsing arguments: {}", err);之后程序就会终止,自然就不会有比如"thread ‘main’ panicked at src/main.rs:26:13:"和"note: run with RUST_BACKTRACE=1 environment variable to display a backtrace"这些内容。

试一下:

$ cargo runCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.48sRunning `target/debug/minigrep`
Problem parsing arguments: not enough arguments

闭包这个概念在下一章才会讲到,这里没看懂也没关系,只要了解个大概即可。

12.4.5. 整体代码

以下是截止到这篇文章写出的所有代码:

use std::env;  
use std::fs;  
use std::process;  struct Config {  query: String,  filename: String,  
}  fn main() {  let args:Vec<String> = env::args().collect();  let config = Config::new(&args).unwrap_or_else(|err| {  println!("Problem parsing arguments: {}", err);  process::exit(1);  });  let contents = fs::read_to_string(config.filename)  .expect("Somthing went wrong while reading the file");  println!("With text:\n{}", contents);  
}  impl Config {  fn new(args: &[String]) -> Result<Config, &'static str> {  if args.len() < 3 {  return Err("Not enough arguments");  }  let query = args[1].clone();  let filename = args[2].clone();  Ok(Config { query, filename})  }  
}

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

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

相关文章

梁山派入门指南4——定时器使用详解,包括定时器中断、PWM产生、输入捕获测量频率

梁山派入门指南4——定时器使用详解&#xff0c;包括定时器中断、PWM产生、输入捕获测量频率 1. 定时器概览2.基本定时器2.1 基本定时器介绍2.2 梁山派上的基本定时器开发2.2.1. 了解梁山派上的基本定时器资源&#xff08;实际上我们以及在上面了解过了&#xff09;2.2.2. 配置…

excel仅复制可见单元格,仅复制筛选后内容

背景 我们经常需要将内容分给不同的人&#xff0c;做完后需要合并 遇到情况如下 那是因为直接选择了整列&#xff0c;当然不可以了。 下面提供几种方法&#xff0c;应该都可以 直接选中要复制区域然后复制&#xff0c;不要选中最上面的列alt;选中可见单元格正常复制&#xff…

JVM类加载器(附面试题)

什么是类加载器 类加载器&#xff08;ClassLoader&#xff09; 是 Java 虚拟机&#xff08;JVM&#xff09;中的一个组件&#xff0c;用于在运行时将字节码文件加载到内存中&#xff0c;并将其转换为 JVM 可以执行的二进制数据结构。 字节码文件通常是以.class为扩展名的文件…

FFmpeg硬件解码

使用FFmpeg进行硬件解码时&#xff0c;通常需要结合FFmpeg的API和硬件加速API&#xff08;如CUDA、VAAPI、DXVA2等&#xff09;。以下是一个简单的C代码示例&#xff0c;展示如何使用FFmpeg进行硬件解码。这个示例使用了CUDA作为硬件加速的后端。 1. 安装FFmpeg和CUDA 确保你…

Linux 进程前篇(冯诺依曼体系结构和操作系统)

目录 一.冯诺依曼体系结构 1.概念 2.硬件层面的数据流 3.总结加补充 二.操作系统 (Operating System) 1.概念 2.设计OS的目的 3.定位 4.操作系统的管理 5.计算机体系的层状结构 在我们认识进程之前&#xff0c;我们先了解什么是冯诺依曼体系结构 一.冯诺依曼体系结构…

Redis 3.2.1在Win10系统上的安装教程

诸神缄默不语-个人CSDN博文目录 这个文件可以跟我要&#xff0c;也可以从官网下载&#xff1a;https://github.com/MicrosoftArchive/redis/releases 这个是微软以前维护的Windows版Redis安装包&#xff0c;如果想要比较新的版本可以从别人维护的项目里下&#xff08;https://…

Unity-Mirror网络框架-从入门到精通之RigidbodyPhysics示例

文章目录 前言示例一、球体的基础配置二、三个球体的设置差异三、示例意图LatencySimulation前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mirror网络框架的深入了解,涵盖从基础到高级的多个主题。Mirror是一个用于Unity的开…

JAVA实现五子棋小游戏(附源码)

文章目录 一、设计来源捡金币闯关小游戏讲解1.1 主界面1.2 黑棋胜利界面1.3 白棋胜利界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/145161039 JA…

深度学习图像算法中的网络架构:Backbone、Neck 和 Head 详解

深度学习已经成为图像识别领域的核心技术&#xff0c;特别是在目标检测、图像分割等任务中&#xff0c;深度神经网络的应用取得了显著进展。在这些任务的网络架构中&#xff0c;通常可以分为三个主要部分&#xff1a;Backbone、Neck 和 Head。这些部分在整个网络中扮演着至关重…

Jmeter如何进行多服务器远程测试

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 JMeter是Apache软件基金会的开源项目&#xff0c;主要来做功能和性能测试&#xff0c;用Java编写。 我们一般都会用JMeter在本地进行测试&#xff0c;但是受到单…

摄像头模块在狩猎相机中的应用

摄像头模块是狩猎相机的核心组件&#xff0c;在狩猎相机中发挥着关键作用&#xff0c;以下是其主要应用&#xff1a; 图像与视频拍摄 高清成像&#xff1a;高像素的摄像头模块可确保狩猎相机拍摄出清晰的图像和视频&#xff0c;能够捕捉到动物的毛发纹理、行为细节及周围环境的…

[操作系统] 深入理解操作系统的概念及定位

概念 任何计算机系统都包含⼀个基本的程序集合&#xff0c;称为操作系统(OS)。 其核心功能如图片所示&#xff0c;包括&#xff1a; 内核 (Kernel)&#xff1a; 内核是操作系统的核心部分&#xff0c;被认为是狭义上的操作系统&#xff0c;直接与硬件打交道。负责进程管理、内…

Jmeter 简单使用、生成测试报告(一)

一、下载Jmter 去官网下载&#xff0c;我下载的是apache-jmeter-5.6.3.zip&#xff0c;解压后就能用。 二、安装java环境 JMeter是基于Java开发的&#xff0c;运行JMeter需要Java环境。 1.下载JDK、安装Jdk 2.配置java环境变量 3.验证安装是否成功&#xff08;java -versio…

postgresql分区表相关问题处理

1.使用pg_cron按日创建分区表&#xff0c;会出现所在数据库对应用户权限不足的问题。 原因是pg_cron运行在postgres数据库中&#xff0c;是用superuser进行执行的&#xff0c;对应的分区表的owner为postgres&#xff0c;所以需要单独授权对表的所有操作权限。不知道直接改变ow…

Agent一键安装,快速上手Zabbix监控!

目录 一、Linux操作系统部署Agent环境配置1、防火墙配置2、永久关闭selinux yum方式安装1、配置zabbix仓库2、安装agent3、配置 Zabbix-Agent 指向 Zabbix-Server4、启动agent服务 二进制包安装1、下载二进制包2、创建用户和目录及更改属主&#xff08;组&#xff09;3、解压二…

内存与缓存:保姆级图文详解

文章目录 前言1、计算机存储设备1.1、硬盘、内存、缓存1.2、金字塔结构1.3、数据流通过程 2、数据结构内存效率3、数据结构缓存效率 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff0c;您的关注是我持续创作的…

【大数据】机器学习-----模型的评估方法

一、评估方法 留出法&#xff08;Holdout Method&#xff09;&#xff1a; 将数据集划分为训练集和测试集两部分&#xff0c;通常按照一定比例&#xff08;如 70% 训练集&#xff0c;30% 测试集&#xff09;。训练集用于训练模型&#xff0c;测试集用于评估模型性能。优点&…

人工智能-机器学习之多分类分析(项目实战二-鸢尾花的多分类分析)

Softmax回归听名字&#xff0c;依然好像是做回归任务的算法&#xff0c;但其实它是去做多分类任务的算法。 篮球比赛胜负是二分类&#xff0c;足球比赛胜平负就是多分类 识别手写数字0和1是二分类&#xff0c;识别手写数字0-9就是多分类 Softmax回归算法是一种用于多分类问题…

[系统安全] 六十一.恶意软件分析 (12)LLM赋能Lark工具提取XLM代码的抽象语法树(初探)

您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列。因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全、逆向分析和恶意代码检测,“系统安全”系列文章会更加聚焦,更加系…

一文掌握Docker

目录 1.快速入门 1.1.部署MySQL 1.2.命令解读 2.Docker基础 2.1.常见命令 2.1.1.命令介绍 2.1.2.演示 2.1.3.命令别名 2.2.数据卷 2.2.1.什么是数据卷 2.2.2.数据卷命令 2.2.3.挂载本地目录或文件 2.3.镜像 2.3.1.镜像结构 2.3.2.Dockerfile构建镜像 2.3.3.构建…