这次的Rust练习是写一个linux命令的grep的操作,代码中有详细的注释。
步骤:
1.接收命令行参数。
2.读取文件。
3.重构:改进模块和错误处理。
4.使用TDD(测试驱动开发)开发库功能。
5.使用环境变量。
6.将错误消息写入标准错误而不是标准输出。
//main.rs
use mingrep::Config;
use std::process;
use std::env;//迭代器有一个方法叫collect,可以将一系列值转化为一个集合。
fn main(){
let args:Vec<String>=env::args().collect();
let config=Config::new(&args).unwrap_or_else(|err|{
eprintln!("Problem parseing arguments:{}",err);
//使用cargo run >output.txt不会出现这些信息,这些错误信息会在终端显示,而不会出现在文件中。
process::exit(1);
});
//使用结构体方法,new。
//unwrap_or_else返回的是如果成功,则把Ok里的放到config。如果失败执行后面的闭包,参数为err的内容。
//程序执行到exit会立即终止,退出,不会打印额外的内容。
if let Err(e)=mingrep::run(config){
eprintln!("Application error:{}",e);
process::exit(1);
}
//就是个运行函数,如果错误则打印错误原因,没错误就不处理。
}
//lib.rs
use std::fs;
use std::error::Error;
use std::env;
pub struct Config{
pub query:String,
pub filename:String,
pub case_sensitive:bool,
}
impl Config{
pub 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();
//将两个变量放入结构体中,没有所有权是不行的,需要clone一下。
let case_sensitive=env::var("CASE_INSENSITIVE").is_err();
//这里的环境变量是通过加在cargo run前面的来实现。
Ok(Config{query,filename,case_sensitive})
//参数少了就返回Err里的字符串,成功就返回Ok里的Config。
}
}
//返回的是:成功就返回空的元组,没有返回。失败返回动态错误类型,自己定义好的Trait。
pub fn run(config:Config)->Result<(),Box<dyn Error>>{
let contents=fs::read_to_string(config.filename)?;
//这里的?就是遇到错误不会发生panic,将错误返回给调用者来处理。
//expect()返回的是result枚举,如果返回的是Ok,就把值取出放在content,如果返回Err,就执行expect
let results=if config.case_sensitive{
search(&config.query,&contents)
}else{
search_case_insensitive(&config.query, &contents)
};
//使用一个bool类型的配置项,选择是否区分大小写。
for line in results{
println!("{}",line);
}
Ok(())
}
//这里的生命周期是返回的str和contents有关,和query无关。
//区分大小写的匹配。
pub fn search<'a>(query:&str,contents:&'a str)->Vec<&'a str>{
let mut results=Vec::new();
for line in contents.lines(){
if line.contains(query){
results.push(line);
}
}
results
}
//不区分大小写的
pub fn search_case_insensitive<'a>(query:&str,contents:&'a str)->Vec<&'a 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="fas";
let contents="\
Rust:
safe,fat,free
pick Fast.";
assert_eq!(vec!["pick Fast."],search_case_insensitive(query,contents));
}
}