【Rust自学】12.7. 使用环境变量
12.7 使用环境变量12.7.0. 写在正文之前第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print)是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。这个项目分为这么几步- 接收命令行参数- 读取文件- 重构改进模块和错误处理- 使用TDD测试驱动开发开发库功能-使用环境变量本文- 将错误信息写入标准错误而不是标准输出12.7.1. 回顾以下是截止到上一篇文章为止所写出的全部代码。lib.rs:use 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)?; for line in search(config.query, contents) { println!({}, line); } Ok(()) } pub fn searcha(query: str, contents: a str) - Veca str { let mut results Vec::new(); for line in contents.lines() { if line.contains(query) { results.push(line); } } results } #[cfg(test)] mod tests { use super::*; #[test] fn one_result() { let query duct; let contents \ Rust: safe, fast, productive. Pick three.; assert_eq!(vec![safe, fast, productive.], search(query, contents)); } }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); } }本文我们将通过添加一个额外的功能来改进minigrep用户可以通过环境变量打开不区分大小写搜索的选项。我们可以将此功能设置为命令行选项并要求用户每次希望应用时输入它但通过将其设置为环境变量我们允许用户设置环境变量一次并在那个终端会话中使所有搜索都不区分大小写。12.7.2. 继续用TDD不区分大小写搜索我们再次遵循TDD的步骤- 编写一个会失败的测试运行该测试并确保它是按照预期的原因失败- 编写或修改刚好足够的代码让新测试通过- 重构刚刚添加或修改的代码确保测试会通过- 返回步骤1继续本篇文章会继续用TDD来实现不区分大小写的搜索。新函数会被命名为search_case_insensitive之后再用环境变量来控制是否启用它。12.7.3. 编写会失败的测试这个对大小写不敏感的函数先给它起个名叫做search_case_insensitive。先把测试模块改一下改出一个对大小写敏感的测试函数和不敏感的测试函数#[cfg(test)] mod tests { use super::*; #[test] fn case_sensitive() { let query duct; let contents \ Rust: safe, fast, productive. Pick three. Duct tape.; assert_eq!(vec![safe, fast, productive.], search(query, contents)); } #[test] fn case_insensitive() { let query rUsT; let contents \ Rust: safe, fast, productive. Pick three. Trust me.; assert_eq!( vec![Rust:, Trust me.], search_case_insensitive(query, contents) ); } }然后再写search_case_insensitive函数的具体内容pub fn search_case_insensitivea(query: str, contents: a str) - Veca str { vec![] }为了让这个函数能被外部函数调用得使用pub来声明为公共的这个函数得加生命周期标志因为有多个非self的参数Rust无法判断哪个参数的生命周期跟返回值的生命周期相同返回值Vector内的元素是字符串切片是从contents截取的所以返回值应和contents的生命周期相同所以给它们两个标注了一样的生命周期a而query则不需要生命周期标注函数内容只需要确保能通过编译即可因为TDD的第一步是编写一个会出错的测试所以出错才是想要的结果这时候跑测试肯定会失败但没关系这正是TDD第一步想要的。12.7.4. 编写或修改刚好足够的代码让新测试通过其实search_case_insensitive的代码与search的大部分都差不多只需要做一些小修改即可。逻辑很好想就是把关键词变成小写再与文本各行的小写形式进行比较pub fn search_case_insensitivea(query: str, contents: a str) - Veca str { let mut results Vec::new(); let query query.to_lowercase(); for line in contents.lines() { if line.to_lowercase().contains(query) { results.push(line); } } results }to_lowercase方法可以把字符串变成全小写to_lowercase转换后的结果是String变量拥有所有权也就是新的query是String而不是str。在循环中的if语句使用的是query因为contains方法不接受String所以得传引用进去再跑一下测试$ cargo test Compiling minigrep v0.1.0 (/tmp/minigrep) Finished test profile [unoptimized debuginfo] target(s) in 0.15s Running unittests src/lib.rs (target/debug/deps/minigrep-dfdfbb86b622af32) running 2 tests test tests::case_sensitive ... ok test tests::case_insensitive ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running unittests src/main.rs (target/debug/deps/minigrep-4c31ade9c6771135) running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests minigrep running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s两个测试都通过了。12.7.5. 在run函数中使用此函数这个函数没问题了就可以在run函数中调用了。但是首先得先为Config结构体添加一个字段来作为使用普通的search还是对大小写不敏感的search_case_insensitive的依据pub struct Config { pub query: String, pub filename: String, pub case_sensitive: bool, }修改run函数让它判断配置pub fn run(config: Config) - Result(), Boxdyn Error { let contents fs::read_to_string(config.filename)?; let results if config.case_sensitive { search(config.query, contents) } else { search_case_insensitive(config.query, contents) }; for line in results { println!({}, line); } Ok(()) }Config上的new这个构造器也得改根据环境变量给case_sensitive这个字段赋值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(); let case_sensitive std::env::var(IGNORE_CASE).is_err(); Ok(Config { query, filename, case_sensitive, }) } }这里使用了std::env::var这个函数当然也可以先把std::env导入作用域再使用env::var它的参数是这个环境变量的名称按惯例全大写这里我写的是IGNORE_CASE中文翻译过来就是忽略大小写。这种环境变量只要出现就认为不区分大小写不出现就认为区分大小写。std::env::var的返回值是Result类型的如果这个IGNORE_CASE环境变量被设置了返回包含环境变量值的Ok(String)反之返回Err(std::env::VarError)。这里std::env::var后面还跟了is_err这个方法如果返回值是Err变体is_err就返回true于是case_sensitive被赋为true反之环境变量已设置则赋false。PS说实话这个小程序写成这个B样也是为了教学的无奈之举我看到一半我都被这个代码量气笑了真正写的时候没必要写得这么一板一眼的12.7.6. 整体代码与试运行写了这么多看看截止到目前的所有代码。lib.rs:use std::error::Error; use std::fs; pub struct Config { pub query: String, pub filename: String, pub case_sensitive: bool, } 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(); let case_sensitive std::env::var(IGNORE_CASE).is_err(); Ok(Config { query, filename, case_sensitive, }) } } pub fn run(config: Config) - Result(), Boxdyn Error { let contents fs::read_to_string(config.filename)?; let results if config.case_sensitive { search(config.query, contents) } else { search_case_insensitive(config.query, contents) }; for line in results { println!({}, line); } Ok(()) } pub fn searcha(query: str, contents: a str) - Veca str { let mut results Vec::new(); for line in contents.lines() { if line.contains(query) { results.push(line); } } results } pub fn search_case_insensitivea(query: str, contents: a str) - Veca str { let mut results Vec::new(); let query query.to_lowercase(); for line in contents.lines() { if line.to_lowercase().contains(query) { results.push(line); } } results } #[cfg(test)] mod tests { use super::*; #[test] fn case_sensitive() { let query duct; let contents \ Rust: safe, fast, productive. Pick three. Duct tape.; assert_eq!(vec![safe, fast, productive.], search(query, contents)); } #[test] fn case_insensitive() { let query rUsT; let contents \ Rust: safe, fast, productive. Pick three. Trust me.; assert_eq!( vec![Rust:, Trust me.], search_case_insensitive(query, contents) ); } }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); } }来试运行一下首先我们将在不设置环境变量的情况下运行程序并使用查询to该查询应与包含全部小写单词to的任何行匹配$ cargo run -- to poem.txt Compiling minigrep v0.1.0 (/tmp/minigrep) Finished dev profile [unoptimized debuginfo] target(s) in 0.07s Running target/debug/minigrep to poem.txt Are you nobody, too? How dreary to be somebody!这次将IGNORE_CASE设置为1其它不变$ IGNORE_CASE1 cargo run -- to poem.txt会得到Are you nobody, too? How dreary to be somebody! To tell your name the livelong day To an admiring bog!没有任何问题。注意如果你在powershell中设置环境变量得这么写PS $Env:IGNORE_CASE1; cargo run -- to poem.txt这会使这个环境变量在这个会话中一直存在如果要去掉这个环境变量写PS Remove-Item Env:IGNORE_CASE

相关新闻

【Rust自学】12.8. 将错误信息写入到标准错误

【Rust自学】12.8. 将错误信息写入到标准错误

12.8 将错误信息写入到标准错误 12.8.0. 写在正文之前 第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print),是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。 这个项目分为这么几步…

2026/7/23 18:57:03阅读更多 →
CNN-LSTM-Attention混合模型在电力负荷预测中的实践

CNN-LSTM-Attention混合模型在电力负荷预测中的实践

1. 项目概述:CNN-LSTM-Attention混合架构的工程实践在电力负荷预测这类典型的时间序列预测场景中,传统统计方法(如ARIMA)往往难以捕捉非线性特征和长期依赖关系。三年前我在参与某省级电网的负荷预测系统升级时,首次尝…

2026/7/23 18:57:03阅读更多 →
基于MATLAB图像处理与神经网络的肺癌智能分类系统

基于MATLAB图像处理与神经网络的肺癌智能分类系统

摘要:开发一种基于图像处理技术和人工神经网络的肺癌智能分类系统,实现肺部医学影像中肿瘤的自动识别与良恶性判别,为临床诊断提供辅助参考。项目概览项目简介本研究采用MATLAB平台开发了一套完整的肺癌分类系统。首先,对肺部CT图…

2026/7/23 18:57:03阅读更多 →
从标准SPI到MibSPI:多缓冲与自动触发如何提升嵌入式通信效率

从标准SPI到MibSPI:多缓冲与自动触发如何提升嵌入式通信效率

1. 项目概述:从标准SPI到MibSPI的演进在嵌入式系统开发中,串行外设接口(SPI)几乎是工程师们最熟悉的老朋友之一。它简单、直接,一个时钟线、两根数据线,加上片选,就能让主控芯片和传感器、存储器…

2026/7/23 20:19:30阅读更多 →
HarmonyOS 无障碍体验实战:朗读标签、焦点顺序与高对比模式

HarmonyOS 无障碍体验实战:朗读标签、焦点顺序与高对比模式

HarmonyOS 无障碍体验实战:朗读标签、焦点顺序与高对比模式 无障碍不是最后补几个标签。读屏用户能不能理解按钮含义,键盘或辅助设备能不能按正确顺序移动焦点,高对比模式下文字是否仍然可读,都会决定用户能否完成任务。本文用一个…

2026/7/23 20:19:30阅读更多 →
【Linux系统编程】进程控制(一)——进程创建与进程终止

【Linux系统编程】进程控制(一)——进程创建与进程终止

文章目录1. 进程创建1.1 fork系统调用1.2 写时拷贝1.3 fork常规用法1.4 fork调用失败的原因2. 进程终止2.1 进程终止的场景2.2 进程常见的退出方法2.2.1 return 退出退出码2.2.2 exitreturn方式 vs 函数exit2.2.3 _exitexit函数 vs 系统调用_exit缓冲区在哪里呢?本节…

2026/7/23 20:19:30阅读更多 →
ICM创芯微 CM1002-C SOT23-6 BMS电池保护芯片

ICM创芯微 CM1002-C SOT23-6 BMS电池保护芯片

特性 高精度电压检测功能 过充电保护电压 3.500V ~ 4.600V,精度 25mV 过充电解除电压 3.100V ~ 4.600V,精度 45mV 过放电保护电压 2.000V ~ 3.400V,精度 50mV 过放电解除电压 2.000V ~ 3.400V,精度 100mV 放电过流保护电压 0.015V…

2026/7/23 20:19:30阅读更多 →
MibSPI寄存器配置全解析:从引脚控制到多缓冲传输实战

MibSPI寄存器配置全解析:从引脚控制到多缓冲传输实战

1. MibSPI控制寄存器概览与设计哲学在嵌入式开发,尤其是汽车电子和工业控制领域,德州仪器的MibSPI模块因其多缓冲架构和并行引脚选项而备受青睐。但很多工程师在初次接触其寄存器手册时,往往会感到困惑:为什么一个SPI模块需要多达…

2026/7/23 20:19:30阅读更多 →
出海AI企业搭建算力基础设施,要满足哪些核心性能要求?

出海AI企业搭建算力基础设施,要满足哪些核心性能要求?

算力出海这件事,前前后后帮客户做了几十次,最关键的底线其实就四条:GPU算力密度、跨区域网络延迟、存储吞吐带宽、安全合规。不同阶段要求不一样,选错直接拉高成本还拖工期。一、AI算力出海,性能瓶颈通常卡在哪大部分问…

2026/7/23 20:17:30阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/23 0:56:31阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/23 0:56:31阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 0:56:31阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:00:28阅读更多 →
从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:28阅读更多 →
油泥处理设备哪里能买到

油泥处理设备哪里能买到

油泥处理设备哪里有?这是许多从事油田、炼化、清罐业务的从业者最关心的问题。根据河南三丰环保设备有限公司的行业经验,选购油泥处理设备的核心在于设备能否适配当地环保法规与原料特性,而非单纯看价格。该公司总经理王钦田先生指出&#xf…

2026/7/23 0:00:28阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/22 22:56:18阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →