【Rust自学】12.3. 重构 Pt.1:改善模块化
12.3.0. 写在正文之前
第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep
(Global Regular Expression Print),是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。
这个项目分为这么几步:
- 接收命令行参数
- 读取文件
- 重构:改进模块和错误处理(本文)
- 使用TDD(测试驱动开发)开发库功能
- 使用环境变量
- 将错误信息写入标准错误而不是标准输出
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
12.3.1. 重构的目的
重构的目的是要增进模块化的程度以及改善错误处理能力。
以下是截止到上一篇文章所写出的全部代码:
use std::env;
use std::fs; fn main() { let args:Vec<String> = env::args().collect(); let query = &args[1]; let filename = &args[2]; println!("search for {}", query); println!("In file {}", filename); let contents = fs::read_to_string(filename) .expect("Somthing went wrong while reading the file");println!("With text:\n{}", contents);
}
这个代码存在4个问题:
-
main
函数负责的功能太多,它既负责命令行的功能解析,又负责读取文件。程序代码的编写原则是每一个函数只负责一个功能,所以说最好把函数拆开。 -
query
和filename
这两个变量是用来存储程序配置的,contents
是用来存储文件内容的。随着代码和变量在编写时越来越多,每个变量的实际意义就变得难以追踪。所以最好把这些变量存在结构体里。 -
读取文件时使用
expect
来处理错误,不论读取时出现了什么错误都只会打印出错误信息并恐慌,这并不是最好的处理方式。因为文件读取失败可能是文件找不到,也有可能是权限问题,现在指定的这个恐慌信息"Somthing went wrong while reading the file"并不能帮助用户排查错误。 -
如果程序里到处都使用
expect
方法那么用户得到的报错信息是来自于Rust语言内部的,比如"Index out of bound",不是程序员根本不明白到底是什么引发了错误。最好是将错误的代码集中放置,从而使将来的维护者在需要修改错误处理相关的逻辑时只考虑这一处代码,也能确保向用户打印的错误信息是易于理解的。
12.3.2. 二进制程序关注点分离的指导性原则
很多Rust二进制项目都会面临同样的组织结构问题,它们将过多的功能和过多的任务都放到了main
函数里面。针对这种情况,Rust社区做了一套为二进制程序进行关注点分离的指导性原则:
- 将程序拆分为
main.rs
和lib.rs
,将业务逻辑放入lib.rs
- 当逻辑较少时,将它放在
main.rs
也可以 - 当逻辑变复杂时,需要将它从
main.rs
提取到lib.rs
经过上述拆分之后,这个例子中应该留在main
函数中的功能有:
- 使用参数值调用命令行解析逻辑
- 进行其它配置
- 调用
lib.rs
中的run
函数 - 处理
run
函数可能出现的问题
12.3.3. 分离逻辑
再看一眼代码:
use std::env;
use std::fs; fn main() { let args:Vec<String> = env::args().collect(); let query = &args[1]; let filename = &args[2]; println!("search for {}", query); println!("In file {}", filename); let contents = fs::read_to_string(filename) .expect("Somthing went wrong while reading the file");println!("With text:\n{}", contents);
}
先把获取命令行参数的部分独立出来:
fn parse_config(args: &[String]) -> (&str, &str) { let query = &args[1]; let filename = &args[2]; (query, filename)
}
&[String]
表示是一个内部元素为String
的Vector
切片- 这里没有打印
query
和filename
的必要了,所以就去掉
然后改一下main
函数,调用parse_config
:
fn main() { let args:Vec<String> = env::args().collect(); let (query, filename) = parse_config(&args); let contents = fs::read_to_string(filename) .expect("Somthing went wrong while reading the file"); println!("With text:\n{}", contents);
}
12.3.4. 使用结构体
parse_config
内把query
和filename
组合成元组返回,在main
函数里又把元组的两个值拆分为两个变量,这种来回拆分合成表明程序中建立的抽象结构有问题。
query
和filename
都是配置的一部分,两者是彼此相关联的,把这两个东西放在元组里不足以表达出这种抽象的关联。最好的办法是放在结构体里:
struct Config { query: String, filename: String,
} fn main() { let args:Vec<String> = env::args().collect(); let config = parse_config(&args); let contents = fs::read_to_string(config.filename) .expect("Somthing went wrong while reading the file"); println!("With text:\n{}", contents);
} fn parse_config(args: &[String]) -> Config { let query = args[1].clone(); let filename = args[2].clone(); Config { query, filename, }
}
parse_config
中必须注意query
和filename
的格式:形参args
的类型是&[String]
是一个引用,没有所有权,所以query
和filename
也是引用,但是Config
这个结构体接收的是String
而不是&String
,所以需要通过克隆来获得所有权,把&String
转为String
。
克隆虽然比直接存储引用消耗了更多时间和内存,但它省去了处理生命周期的麻烦,让代码更加直接简单。在某些场景中,放弃一些性能来获取更多的简洁性是非常值得考虑的。
当然,使用String::from
函数来封装也是可以的:
fn parse_config(args: &[String]) -> Config { let query = &args[1]; let filename = &args[2]; Config { query: String::from(query), filename: String::from(filename), }
}
当然可行的代码可能不止这两种,这里我就采用第一种克隆的方法。
12.3.5. 把函数变为结构体的方法
既然parse_config
会创建一个Config
的实例,也就是说它是一个构造函数。对于构造函数,可以这么写:
impl Config { fn new(args: &[String]) -> Config { let query = args[1].clone(); let filename = args[2].clone(); Config { query, filename, } }
}
只需要把这个函数写在Config
的方法上即可(对于方法的详细解释,详见 5.3. struct的方法(Method))。这里还给parse_config
改了个名叫new
,是因为我把它当作了一个构造函数来处理(构造函数一般都命名为new
)。
这么改,main
函数里面也需要改一下:
let config = Config::new(&args);
12.3.5. 整体代码
以下是截止到本篇文章所写出的所有代码:
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, } }
}
相关文章:
【Rust自学】12.3. 重构 Pt.1:改善模块化
12.3.0. 写在正文之前 第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print),是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。 这个项目分为这么几步: 接收命令行参数读取…...
Cosmos:英伟达发布世界基础模型,为机器人及自动驾驶开发加速!
1. 简介 在2025年消费电子展(CES)上,NVIDIA发布了全新的Cosmos平台,旨在加速物理人工智能(AI)系统的开发,尤其是自主驾驶车辆和机器人。该平台集成了生成式世界基础模型(WFM&#x…...
【Docker】保姆级 docker 容器部署 MySQL 及 Navicat 远程连接
🥰🥰🥰来都来了,不妨点个关注叭! 👉博客主页:欢迎各位大佬!👈 文章目录 1. docker 容器部署 MySQL1.1 拉取mysql镜像1.2 启动容器1.3 进入容器1.4 使用 root 用户登录 2. Navicat 连…...
Java IDEA中Gutter Icons图标的含义
前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。 👉点击跳转到教程 前言: 很多人刚开始用IDEA来学习编程,会发现下面这些图标。 但是…...
Broker收到消息之后如何存储
1.前言 此文章是在儒猿课程中的学习笔记,感兴趣的想看原来的课程可以去咨询儒猿课堂《从0开始带你成为RocketMQ高手》,我本人觉得这个作者还是不错,都是从场景来进行分析,感觉还是挺适合我这种小白的。这块主要都是我自己的学习笔…...
RuoYi框架上传图片或文件到阿里云OSS详细教程
为了提供一个更加详细的教程,我们将深入探讨每个步骤,并添加一些额外的细节和最佳实践建议。以下是关于如何在Ruoyi框架中集成阿里云OSS实现文件上传功能的详尽指南。 详细教程 环境准备 注册阿里云账号:访问阿里云官网并创建一个账户。创…...
【论文笔记】SmileSplat:稀疏视角+pose-free+泛化
还是一篇基于dust3r的稀疏视角重建工作,作者联合优化了相机内外参与GS模型,实验结果表明优于noposplat。 abstract 在本文中,提出了一种新颖的可泛化高斯方法 SmileSplat,可以对无约束(未标定相机的)稀疏多…...
python实现收到一封邮件时自动触发执行读取邮件内容及后续操作
要实现收到一封邮件时自动触发执行 getEmailData(),可以结合定时任务或实时事件监控机制来实现。以下是两种常用的方法: 方法 1:轮询方式(定时检测) 使用 schedule 或 time.sleep 循环定期检测收件箱: i…...
【Vim Masterclass 笔记12】S06L26 + L27:Vim 文本的搜索、查找及替换同步练习(含点评课)
文章目录 S06L26 Exercise 07 - Search, Find, and Replace1 训练目标2 操作指令2.1. 打开 search-practice.txt 文件2.2. 同一行内的搜索练习2.3. 当前文件内的搜索练习2.4. 单词搜索练习2.5. 全局替换练习 3 退出 Vim S06L27 同步练习点评课 写在前面 Vim 的文本检索、查找与…...
YOLOv11 OBB 任务介绍与数据集构建要求及训练脚本使用指南
YOLO(You Only Look Once)是一个高效且广泛应用于目标检测任务的深度学习框架。在目标检测任务中,传统的边界框(AABB)通过四个参数来定义目标的位置信息:中心坐标、宽度、高度以及目标的旋转角度。然而&…...
Leecode刷题C语言之超过阈值的最小操作数②
执行结果:通过 执行用时和内存消耗如下: // 最小堆的节点结构体 typedef struct {long long* heap;int size;int capacity; } MinHeap;// 初始化最小堆 MinHeap* createMinHeap(int capacity) {MinHeap* minHeap (MinHeap*)malloc(sizeof(MinHeap));minHeap->s…...
【Linux】11.Linux基础开发工具使用(4)
文章目录 3. Linux调试器-gdb使用3.1 背景3.2 下载安装3.3 使用gdb查询3.4 开始使用 3. Linux调试器-gdb使用 3.1 背景 程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序,默认是release模式 要使用gdb调试,必须…...
Cesium中的CustomDataSource 详解
Cesium CustomDataSource 详解 在 Cesium 中,CustomDataSource 是一个强大的类,用于处理自定义的地理数据。它提供了一种方法,可以通过程序方式添加、管理和更新动态的地理实体,而无需依赖外部数据格式(如 GeoJSON 或…...
win32汇编环境,窗口程序中组合框的应用举例
;运行效果 ;win32汇编环境,窗口程序中组合框的应用举例 ;比如在窗口程序中生成组合框,增加子项,删除某项,取得指定项内容等 ;直接抄进RadAsm可编译运行。重点部分加备注。 ;以下是ASM文件 ;>>>>>>>>>>>>…...
Wireshark 使用教程:网络分析从入门到精通
一、引言 在网络技术的广阔领域中,网络协议分析是一项至关重要的技能。Wireshark 作为一款开源且功能强大的网络协议分析工具,被广泛应用于网络故障排查、网络安全检测以及网络协议研究等诸多方面。本文将深入且详细地介绍 Wireshark 的使用方法&#x…...
菜品管理(day03)
公共字段自动填充 问题分析 业务表中的公共字段: 而针对于这些字段,我们的赋值方式为: 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。 在更新数据时, 将updateTime 设置为当前时间…...
Scira - 一个极简的开源 AI 搜索引擎
支持实时搜索 、学术论文分析 、社交媒体洞察 、YouTube 搜索 、航班追踪 、电影搜索,功能倒是挺多。 但是目前只支持 xAI 的 Grok 还不能换模型,不过用的 Vercel SDK 支持下 DeepSeek 应该很容易 https://index.html.zone/ai/scira...
利用源码安装httpd
方法一: 1,下载源码 [rootopenEuler-1 ~]# wget https://archive.apache.org/dist/httpd/httpd-2.4.46.tar.gz [rootopenEuler-1 ~]# ls anaconda-ks.cfg httpd-2.4.46.tar.gz mysql-8.0.36-linux-glibc2.12-x86_64.tar.xz 2,进行压缩 […...
软件测试 —— Selenium(等待)
软件测试 —— Selenium(等待) 一个例子强制等待使用示例:为什么不推荐使用强制等待?更好的选择 隐式等待 implicitly_wait()隐式等待和强制等待的区别隐式等待(Implicit Wait)强制等…...
图像模糊度(清晰度)检测 EsFFT 算法详细分析
图像模糊度检测算法 基于频域的算法 傅里叶变换法:先将图像进行傅里叶变换得到频谱图,频谱图中心为低频,向外扩展为高频。通过屏蔽频谱图中心区域实现高通滤波,保留图像边缘等高频信息,再求频谱图的均值即平均高频幅值,该值越小,图像越模糊。但传统FFT方法存在不足,如…...
快速上手 HarmonyOS 应用开发
一、DevEco Studio 安装与配置 1. DevEco Studio 简介 DevEco Studio 是 HarmonyOS 的一站式集成开发环境(IDE),提供了丰富的工具和功能,支持 HarmonyOS 应用开发的全流程。 2. DevEco Studio 下载与安装 下载地址:…...
金融项目实战 06|Python实现接口自动化——日志、实名认证和开户接口
目录 一、日志封装及应用(理解) 二、认证开户接口脚本编写 1、代码编写 1️⃣api目录 2️⃣script目录 2、BeautifulSoup库 1️⃣简介及例子 2️⃣提取html数据工具封装 3、认证开户参数化 一、日志封装及应用(理解) &…...
Lianwei 安全周报|2025.1.13
新的一周又开始了,以下是本周「Lianwei周报」,我们总结推荐了本周的政策/标准/指南最新动态、热点资讯和安全事件,保证大家不错过本周的每一个重点! 政策/标准/指南最新动态 01 美国国土安全部发布《公共部门生成式人工智能部署手…...
【C#深度学习之路】如何使用C#实现Yolo8/11 Segment 全尺寸模型的训练和推理
【C#深度学习之路】如何使用C#实现Yolo8/11 Segment 全尺寸模型的训练和推理 项目背景项目实现推理过程训练过程 项目展望写在最后项目下载链接 本文为原创文章,若需要转载,请注明出处。 原文地址:https://blog.csdn.net/qq_30270773/article…...
【Compose multiplatform教程】05 IOS环境编译
了解如何使现有的 Android 应用程序跨平台,以便它在 Android 和 iOS 上都能运行。您将能够在一个位置编写代码并针对 Android 和 iOS 进行测试一次。 本教程使用一个示例 Android 应用程序,其中包含用于输入用户名和密码的单个屏幕。凭证经过验证并保存…...
【声音场景分类--论文阅读】
1.基于小波时频图特征在声音场景分类 基于小波时频图特征在声音场景分类任务中的表现 2.增强增强高效音频分类网络 https://arxiv.org/pdf/2204.11479v5 https://github.com/Alibaba-MIIL/AudioClassfication 音频分类网络如图4所示。在此阶段,主要重点是建立一…...
浅谈云计算02 | 云计算模式的演进
云计算计算模式的演进 一、云计算计算模式的起源追溯1.2 个人计算机与桌面计算 二、云计算计算模式的发展阶段2.1 效用计算的出现2.2 客户机/服务器模式2.3 集群计算2.4 服务计算2.5 分布式计算2.6 网格计算 三、云计算计算模式的成熟与多元化3.1 主流云计算服务模式的确立3.1.…...
【专题】2025年节日营销趋势洞察报告汇总PDF洞察(附原数据表)
原文链接: https://tecdat.cn/?p38813 在当今复杂多变且竞争激烈的消费市场环境下,节日营销已成为企业获取市场份额、提升品牌影响力的关键战略时机。我们深知深入洞察节日营销趋势对于企业决策的重要性。 本报告汇总基于对 2024 年多个关键消费节点及…...
AR 在高校实验室安全教育中的应用
AR应用APP可以内置实验室安全功能介绍,学习并考试(为满足教育部关于实验室人员准入条件),AR主模块。其中AR主模块应该包括图形标识码的扫描,生成相应模型,或者火灾、逃生等应急处置的路线及动画演示。考试采…...
PHP智慧小区物业管理小程序
🌟智慧小区物业管理小程序:重塑社区生活,开启便捷高效新篇章 🌟 智慧小区物业管理小程序是一款基于PHPUniApp精心雕琢的智慧小区物业管理小程序,它犹如一股清新的科技之风,吹进了现代智慧小区的每一个角落…...
使用防抖与节流优化 Vue 中的异步函数调用
使用防抖与节流优化 Vue 中的异步函数调用 在 Vue 项目中,我们经常需要处理用户交互事件,例如点击、输入、切换复选框等。这些事件可能频繁触发,尤其在用户快速操作的情况下,如果每次触发都执行复杂的逻辑(如异步网络…...
【大厂面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据...本篇介绍自动驾驶检测模型如何针对corner case 优化?
【大厂面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据…本篇介绍自动驾驶检测模型如何针对corner case 优化? 【大厂面试AI算法题中的知识点】方向涉及:ML/DL/CV/NLP/大数据…本篇介绍自动驾驶检测模型如何针对corner case 优化&…...
Android CustomTextField
在 Compose 中开发用户界面时,需要处理输入框和键盘的交互,例如在键盘弹出时调整布局位置,避免遮挡重要内容。本篇博客将通过一个完整的示例展示如何实现这一功能。 功能概述 本例实现了一个简单的输入框。当输入框获得焦点或输入文字时&…...
源码编译安装httpd 2.4,提供系统服务管理脚本并测试(两种方法实现)
一、源码编译安装httpd 2.4 # 从官网下载httpd源代码 [rootopenEuler-2 ~]# wget https://downloads.apache.org/httpd/httpd-2.4.62.tar.gz# 解压并进入到该目录中 [rootopenEuler-2 ~]# tar -zxvf httpd-2.4.62.tar.gz [rootopenEuler-2 ~]# cd httpd-2.4.62/# 安装httpd编译…...
ubuntu24.04安装docker显卡工具包nvidia-container-toolkit
问题描述 docker 容器启动时如果需要访问 gpu ,需要安装 nvidia-container-toolkit 才行,否则会提示如下错误 sudo docker run --rm -it --gpus all ubuntu:latest docker: Error response from daemon: could not select device driver "" …...
mac intel芯片下载安卓模拟器
一、调研 目前主流两个模拟器: 雷神模拟器 不支持macosmumu模拟器pro版 不支持macos intel芯片 搜索到mumu的Q&A中有 “Intel芯片Mac如何安装MuMu?” q&a🔗:https://mumu.163.com/mac/faq/install-on-intel-mac.html 提…...
4 原型(Protoytpe)模式
原型模式 1.1 分类 (对象)创建型 1.2 提出问题 希望复制一个状态完全相同的对象。首先,新建一个相同类的对象。 然后,复制所有成员变量。 但是,有时候不知道具体类型,而且成员变量可能是私有的。&#…...
kafka的listeners和advertised.listeners,配置内外网分流
总结: listeners 指明 kafka 当前节点监听本机的哪个网卡 advertised.listeners 指明客户端通过哪个 ip 可以访问到当前节点 内网和外网并不必须是是我们通常说的公司内部网络和公网,只要是两块网卡都可以,不管是这两块网卡是公网、内网、甚至…...
Mac——Docker desktop安装与使用教程
摘要 本文是一篇关于Mac系统下Docker Desktop安装与使用教程的博文。首先介绍连接WiFi网络,然后详细阐述了如何在Mac上安装Docker,包括下载地址以及不同芯片版本的选择。接着讲解了如何下载基础镜像和指定版本镜像,旨在帮助用户在Mac上高效使…...
Redis十大数据类型详解
Redis(一) 十大数据类型 redis字符串(String) string是redis最基本的类型,一个key对应一个value string类型是二进制安全的,意思是redis的string可以包含任何数据。例如说是jpg图片或者序列化对象 一个re…...
.net core 中使用AsyncLocal传递变量
官网 https://github.com/dotnet/runtime/blob/16b6369b7509e58c35431f05681a9f9e5d10afaa/src/libraries/System.Private.CoreLib/src/System/Threading/AsyncLocal.cs#L45 AsyncLocal是一个在.NET中用来在同步任务和异步任务中保持全局变量的工具类。它允许你在不同线程的同…...
C#Halcon视觉流程框架个人封装流程心得
一,实现效果 1,初始界面 2,加载流程 3,点击流程列表“加载2D图像" 4,设置图像预处理参数与画线找线 5,执行流程 6,折叠工具箱 7,折叠操作区域 二,实现流程 1&…...
web第一次作业
系统登录代码: <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>第一次作业</title…...
Kylin Linux V10 替换安装源,并在服务器上启用 EPEL 仓库
查看系统版本: cat /etc/os-releaseNAME"Kylin Linux Advanced Server" VERSION"V10 (Lance)" ID"kylin" VERSION_ID"V10" PRETTY_NAME"Kylin Linux Advanced Server V10 (Lance)" ANSI_COLOR"0;31"u…...
备战蓝桥杯:树的存储与遍历(dfs和bfs)
树的概念 树的逻辑结构是树形结构,和我们之前的线性结构又不太一样了,是一种一对多的关系 树的结点分为根节点,叶子结点(没有分支的结点) 以及分支结点 从上往下看,每个结点都有0个或多个后继 从下往上…...
[Deep Learning] Anaconda+CUDA+CuDNN+Pytorch(GPU)环境配置-2025
文章目录 [Deep Learning] AnacondaCUDACuDNNPytorch(GPU)环境配置-20250. 引子1. 安装Anaconda1.1 安装包下载:1.2 启用安装包安装1.3 配置(系统)环境变量1.4 验证Anaconda是否安装完毕1.5 Anaconda换源 2. 安装CUDACuDNN2.1 判断本机的CUDA版本2.2 下载适合自己CU…...
计算机的错误计算(二百一十二)
摘要 利用两个大模型计算 实验表明,两个大模型均进行了中肯的分析。另外,其中一个大模型给出了 Python代码,运行后,结果中有7位错误数字;而一个大模型进行加减运算时出错。 例1. 计算 下面是与一个大模型的对话…...
Inxpect毫米波安全雷达:精准检测与动态保护,工业自动化可靠选择
Inxpect毫米波安全雷达具备“精准检测、动态区域保护、环境适应性”三大核心功能。在工业自动化和机器人系统里,这些功能发挥着重要作用,有助于提升安全性与效率。Inxpect雷达运用毫米波技术,在诸如存在灰尘、烟雾或碎屑等复杂环境中,也能保持…...
springboot房屋租赁管理系统
Spring Boot房屋租赁管理系统是一种基于Spring Boot框架构建的,旨在解决传统租房市场中房源信息更新不及时、虚假信息泛滥、交易流程繁琐等问题的信息化解决方案。 一、系统背景与目的 随着城市化进程的加快和人口流动性的增强,租房市场需求急剧增长。…...
如何使用wireshark 解密TLS-SSL报文
目录 前言 原理 操作 前言 现在网站都是https 或者 很多站点都支持 http2。这些站点为了保证数据的安全都通过TLS/SSL 加密过,用wireshark 并不能很好的去解析报文,我们就需要用wireshark去解密这些报文。我主要讲解下mac 在 chrome 怎么配置的&…...