1 接受命令行参数
- 用
std::env::args()方法获取命令行参数,得到的是std::env::Args类型,它实现了单向(Iterator)、双向迭代器(DoubleEndedIterator) - 用
Iterator的collect()方法可以将迭代器中的元素收集到一个集合对象中,如Vector - 将
Iterator的collect()方法返回值赋值给变量时,通常需要指明变量类型,因为方法返回泛型类型,通常无法自动推导数据类型
use std::env;pub fn main() {let args : Vec<String> = env::args().collect();println!("{:?}",args);let filename = &args[1];let query = &args[2];println!("filename: {}",filename);println!("query: {}",query);}
2 读取文件
use std::env;use std::fs::File;use std::io::prelude::*;use std::error::Error;pub fn main2() -> Result<String,Box<Error>>{let args : Vec<String> = env::args().collect();println!("{:?}",args);let filename = &args[1];let query = &args[2];println!("filename: {}",filename);println!("query: {}",query);let mut content = String::new();File::open(filename)?.read_to_string(&mut content)?;Ok(content)}
3 重构
对二进制程序进行关注分离的建议过程:
- 将程序拆分成
main.rs和lib.rs,将程序逻辑放入lib.rs中 - 当命令行解析逻辑比较小时,可以保留在
main.rs中 - 当命令行解析逻辑变得复杂时,也将其从
main.rs中提取到lib.rs中 - 经过上述步骤之后,
main函数的责任应该被限制为:- 使用参数值调用命令行解析逻辑
- 设置任何其他配置
- 调用
lib.rs中的run函数 - 如果
run函数返回错误,则处理这个错误
3.1 提取参数解析器
pub fn main() {let args: Vec<String> = env::args().collect();let (filename, query) = parse_config(&args);………………}fn parse_config(args: &[String]) -> (&str, &str) {let filename = &args[1];let query = &args[2];(filename, query)}
3.2 组合配置值
struct Config {filename: String,query: String,}pub fn main() {let args: Vec<String> = env::args().collect();let config = parse_config(&args);…………}fn parse_config(args: &[String]) -> Config {// 这里如果不使用clone,则涉及较复杂的生命周期,对于简单的程序,将得不偿失let filename = args[1].clone();let query = args[2].clone();Config {filename: filename,query: query,}}
3.3 为配置增加构造函数
struct Config {filename: String,query: String,}pub fn main() {let args: Vec<String> = env::args().collect();let config = Config::new(&args);…………}impl Config {fn new(args: &[String]) -> Config {let filename = args[1].clone();let query = args[2].clone();Config {filename: filename,query: query,}}}
3.4 改进错误处理
struct Config {filename: String,query: String,}pub fn main() {let args: Vec<String> = env::args().collect();// 调用 Result类型的 unwrap_or_else() 方法let config = Config::new(&args).unwrap_or_else(|err|{println!("解析参数失败: {}",err);process::exit(1);});…………………………}impl Config {// 返回值变成了 Result 类型fn new(args: &[String]) -> Result<Config,&'static str>{if args.len() < 3 {return Err("参数太少")// 这里返回Err}let filename = args[1].clone();let query = args[2].clone();// 这里返回 Result 类型Ok(Config {filename: filename,query: query,})}}
3.5 从main中提取逻辑
pub fn main() {let args: Vec<String> = env::args().collect();// 调用 Result类型的 unwrap_or_else() 方法let config = Config::new(&args).unwrap_or_else(|err|{println!("解析参数失败: {}",err);process::exit(1);});// 注意 if let 的使用if let Err(err) = run(config){println!("读取文件失败: {}",err);process::exit(2);}}fn run(config : Config) -> Result<(),Box<Error>>{let mut content = String::new();// 注意 ? 运算符的使用File::open(config.filename)?.read_to_string(&mut content)?;println!("With text:\n{}", content);Ok(())}
4 将代码拆分到crate
// 这是 main.rsuse std::env as vne;use std::process;extern crate chap12;fn main(){section3_step7();}fn section3_step7() {let args: Vec<String> = vne::args().collect();println!("{:?}", args);// unwrap_or_elselet config = chap12::Config::new(&args).unwrap_or_else(|err|{eprintln!("解析参数失败: {}",err);process::exit(1);});println!("文件: {} 查找: {}",config.filename,config.query);// if let 的使用if let Err(err) = chap12::run(config){eprintln!("读取文件失败: {}",err);process::exit(2);}}
// 这是 lib.rsuse std::fs::File;use std::io::prelude::*;use std::error::Error;pub struct Config {pub filename: String,pub query: String,pub case_insensitive: bool,}pub fn run(config : Config) -> Result<(),Box<Error>>{let mut content = String::new();// ? 运算符的使用let mut file = File::open(config.filename)?.read_to_string(&mut content)?;println!("With text:\n{}", content);// if 表达式let result = if config.case_insensitive{search(&config.query,&content)}else{search_insensitive(&config.query,&content)};println!("结果: {:?}",result);Ok(())}impl Config {pub fn new(args: &[String]) -> Result<Config,&'static str>{if args.len() < 3 {return Err("参数太少")}let filename = args[1].clone();let query = args[2].clone();Ok(Config {filename: filename,query: query,case_insensitive: std::env::var("CASE_INSENSITIVE").is_err(),})}}pub fn search<'a>(query:&str,content:&'a str) -> Vec<&'a str>{let mut result = Vec::new();for line in content.lines(){if line.contains(query){result.push(line);}}result}pub fn search_insensitive<'a>(query:&str,content:&'a str) -> Vec<&'a str>{let mut result = Vec::new();let query = query.to_lowercase();for line in content.lines(){if line.to_lowercase().contains(&query){result.push(line);}}result}
