【Rust自学】12.5. 重构 Pt.3:移动业务逻辑
12.5 重构 Pt.3移动业务逻辑12.5.0. 写在正文之前第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print)是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。这个项目分为这么几步- 接收命令行参数- 读取文件-重构改进模块和错误处理本文- 使用TDD测试驱动开发开发库功能- 使用环境变量- 将错误信息写入标准错误而不是标准输出12.5.1. 回顾之前两节分别做了模块化的优化和错误处理这节在此基础上还要做进一步的优化。以下是截止到上一篇文章所写出的全部代码use std::env; use std::fs; use std::process; struct Config { query: String, filename: String, } fn main() { let args:VecString 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(Something went wrong while reading the file); println!(With text:\n{}, contents); } impl Config { fn new(args: [String]) - ResultConfig, 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}) } }12.5.2. 从main函数中提取逻辑在 12.3. 重构 Pt.1 中说过二进制程序关注点分离的指导性原则- 将程序拆分为main.rs和lib.rs将业务逻辑放入lib.rs- 当逻辑较少时将它放在main.rs也可以- 当逻辑变复杂时需要将它从main.rs提取到lib.rs根据上述拆分原则我们应该把main函数里所有除了配置解析和错误处理之外的所有逻辑单独提取到一个run函数里。把main函数精简到足以通过阅读代码来检查正确性而其他的逻辑就可以通过测试验证了对于测试这部分的内容详见 11.1. 编写和运行测试。对于这个截止到目前的代码run函数应该是fn run(config: Config) { let contents fs::read_to_string(config.filename) .expect(Something went wrong while reading the file); println!(With text:\n{}, contents); }main函数里也改为通过调用run函数来读取fn main() { let args:VecString env::args().collect(); let config Config::new(args).unwrap_or_else(|err| { println!(Problem parsing arguments: {}, err); process::exit(1); }); run(config); }12.5.3. 改善run函数的错误处理现在的run函数对于读取错误的情况采用的是expect。而这种错误处理会调用panic!我们需要的是像Config::new这样使用Result类型来传播错误就应该这么写fn run(config: Config) - Result(), Boxdyn Error { let contents fs::read_to_string(config.filename)?; println!(With text:\n{}, contents); Ok(()) }Result类型的Ok对应的是()类型单元类型这种类型表示什么也不返回什么也没有因为run函数正确执行确实什么都不需要返回。这个函数体的最后一行Ok()里加了()就代表返回Ok变体并且包裹了一个单元类型。Result的Err对应的是Boxdyn Error这个东西你暂且不需要深入了解只需要知道它代表所有实现了std::error::Error这个trait的类型这里只写了Error是因为我在代码开头写了use std::error::Error;把它引入了作用域但是不需要指定具体的类型。这意味着在不同的场景下可以返回不同的错误类型。dyn是dynamic动态一词的简写。?这个符号在 9.3. Result枚举与可恢复的错误 Pt.2 中有详细讲过这里就再简单讲一下read_to_string的返回值是Result类型。加了?表示如果read_to_string的返回值是Ok,就把Ok所关联的值返回赋值给变量如果是Err那么会直接终止这个函数的运行把Err及其所附带的错误信息返回。也就是说加?的效果等同于let contents match fs::read_to_string(config.filename){ Ok(contents) contents, Err(e) return Err(e.into()), };e.into()会把io::Error转换成Boxdyn Error这样返回类型才能对上?运算符会替你完成这种转换。这么改之后就会把错误传播给调用者也就是main函数所以在main函数里得处理可能出现的错误fn main() { let args:VecString env::args().collect(); let config Config::new(args).unwrap_or_else(|err| { println!(Problem parsing arguments: {}, err); process::exit(1); }); if let Err(e) run(config) { println!(Application error: {}, e); process::exit(1); } }这里使用到的if let是match的一个语法糖把它理解为只处理一种分支的match即可详细可见 6.4. 简单的控制流-if let。需要强调if let和if不是同一回事不要把它们相提并论。12.5.4. 迁移业务逻辑现在我们完成了所有函数的独立和错误处理接下来要做的就是把它们移到lib.rs里。迁移的对象就是这些函数、结构体和相关的引用。迁移后的成果lib.rsuse std::error::Error; use std::fs; pub struct Config { pub query: String, pub filename: String, } impl Config { pub fn new(args: [String]) - ResultConfig, 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}) } } pub fn run(config: Config) - Result(), Boxdyn Error { let contents fs::read_to_string(config.filename)?; println!(With text:\n{}, contents); Ok(()) }注意所有的被main.rs使用的结构体、结构体上的方法和函数都得在声明时加pub关键字来声明为公共的才能被调用。再看看main.rs:use std::env; use std::process; use minigrep::Config; fn main() { let args:VecString env::args().collect(); let config Config::new(args).unwrap_or_else(|err| { println!(Problem parsing arguments: {}, err); process::exit(1); }); if let Err(e) minigrep::run(config) { println!(Application error: {}, e); process::exit(1); } }所有的重构任务已经完成下一步就是编写测试12.6. 使用TDD(测试驱动开发)开发库功能。

相关新闻

智慧工地安全帽检测系统:YOLOv5与边缘计算实践

智慧工地安全帽检测系统:YOLOv5与边缘计算实践

1. 项目背景与核心价值在建筑施工现场,安全帽是保护工人头部安全的最后一道防线。根据行业统计数据显示,超过60%的头部伤害事故都与未正确佩戴安全帽有关。传统的人工巡检方式存在监管盲区大、响应滞后等问题,而"智慧工地安全帽佩戴检测…

2026/10/18 10:36:33 阅读更多 →
每日关注简报|2026年7月23日:Copilot成效仪表盘、Codex iOS升级、Windows 11新功能与AI财报

每日关注简报|2026年7月23日:Copilot成效仪表盘、Codex iOS升级、Windows 11新功能与AI财报

🔥 个人主页: 杨利杰YJlio ❄️ 个人专栏: 《Windows 疑难杂症与工单复盘案例库》 《Sysinternals实战教程》 《WINDOWS教程》 《Windows PowerShell 实战》 《IOS插件分析测试》 《超简单:用Python让Excel飞起来》…

2026/10/18 10:36:47 阅读更多 →
【计算机Python毕业设计案例】基于 Python 的期货模拟下单与盈亏统计系统设计与实现 金融课程配套期货模拟交易实训系统(程序+文档+讲解+定制)

【计算机Python毕业设计案例】基于 Python 的期货模拟下单与盈亏统计系统设计与实现 金融课程配套期货模拟交易实训系统(程序+文档+讲解+定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/10/18 10:36:49 阅读更多 →

最新新闻

零基础也能创建AI智能体:三步搭建个人助手实战指南

零基础也能创建AI智能体:三步搭建个人助手实战指南

身边很多朋友一听到“AI智能体”这个词,第一反应都是:这玩意儿是不是得先学三个月Python,再搞懂什么API调用、微调模型,才配碰一碰?其实真不是。我刚开始接触的时候也抱着同样的刻板印象,直到我自己用纯聊天…

2026/10/18 16:49:44 阅读更多 →
JSP篇之JSP基础语法

JSP篇之JSP基础语法

JSP简介 (图片来源:慕课网) 常见的动态网站开发技术对比 (图片来源:慕课网) JSP页面元素简介以及page指令 JSP元素的构成 (图片来源:慕课网) 指令: JSP指…

2026/10/18 16:49:43 阅读更多 →
佳能相机SDK开发指南:从工业视觉接入到RAW实时处理

佳能相机SDK开发指南:从工业视觉接入到RAW实时处理

简介:本资源为佳能官方相机SDK开发套件,面向Windows平台C/VB开发者,用于构建远程控制、参数调节与图像采集类摄影应用,解决专业相机集成开发中API调用、库链接与环境配置等核心问题。压缩包共854个文件,174.44MB&#…

2026/10/18 16:49:43 阅读更多 →
高通平台音频驱动实战:从ASoC到音频DSP的完整链路

高通平台音频驱动实战:从ASoC到音频DSP的完整链路

刚接手高通平台安卓音频驱动开发时,我一度以为主要工作是改内核设备树、调codec寄存器。真正动手才发现,最耗时间的不是写代码,而是先把一条声音从APP到喇叭的完整链路弄清楚。高通平台的音频驱动和教科书里标准Linux ASoC架构有挺大区别&…

2026/10/18 16:49:43 阅读更多 →
ABS轮速传感器原理与故障排查:从磁电式到霍尔式的信号解读

ABS轮速传感器原理与故障排查:从磁电式到霍尔式的信号解读

简介:这是一份系统讲解ABS车轮速度传感器基本结构与工作原理的技术文档,适合汽车维修技师、汽车电子专业学生及对ABS系统感兴趣的从业者学习参考。资源包内含1个doc文档,压缩包整体仅226KB,聚焦轮速传感器的核心知识模块&#xff…

2026/10/18 16:49:43 阅读更多 →
Nacos 2.4.3 服务端部署实战:从单机到集群的落地指南

Nacos 2.4.3 服务端部署实战:从单机到集群的落地指南

简介:nacos-server-2.4.3.zip 是面向微服务开发者与运维人员的 Nacos 服务端发行包,对应 2.4.3 版本,可用于搭建服务注册与发现、动态配置管理及服务元数据管理平台,兼容 Spring Cloud、Dubbo、Kubernetes 等主流微服务生态&#…

2026/10/18 16:48:43 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →