1 接受命令行参数

  • std::env::args()方法获取命令行参数,得到的是std::env::Args类型,它实现了单向(Iterator)、双向迭代器(DoubleEndedIterator
  • Iteratorcollect()方法可以将迭代器中的元素收集到一个集合对象中,如Vector
  • Iteratorcollect()方法返回值赋值给变量时,通常需要指明变量类型,因为方法返回泛型类型,通常无法自动推导数据类型
  1. use std::env;
  2. pub fn main() {
  3. let args : Vec<String> = env::args().collect();
  4. println!("{:?}",args);
  5. let filename = &args[1];
  6. let query = &args[2];
  7. println!("filename: {}",filename);
  8. println!("query: {}",query);
  9. }

2 读取文件

  1. use std::env;
  2. use std::fs::File;
  3. use std::io::prelude::*;
  4. use std::error::Error;
  5. pub fn main2() -> Result<String,Box<Error>>{
  6. let args : Vec<String> = env::args().collect();
  7. println!("{:?}",args);
  8. let filename = &args[1];
  9. let query = &args[2];
  10. println!("filename: {}",filename);
  11. println!("query: {}",query);
  12. let mut content = String::new();
  13. File::open(filename)?.read_to_string(&mut content)?;
  14. Ok(content)
  15. }

3 重构

对二进制程序进行关注分离的建议过程:

  1. 将程序拆分成main.rslib.rs,将程序逻辑放入lib.rs
  2. 当命令行解析逻辑比较小时,可以保留在main.rs
  3. 当命令行解析逻辑变得复杂时,也将其从main.rs中提取到lib.rs
  4. 经过上述步骤之后,main函数的责任应该被限制为:
    • 使用参数值调用命令行解析逻辑
    • 设置任何其他配置
    • 调用lib.rs中的run函数
    • 如果run函数返回错误,则处理这个错误

3.1 提取参数解析器

  1. pub fn main() {
  2. let args: Vec<String> = env::args().collect();
  3. let (filename, query) = parse_config(&args);
  4. ………………
  5. }
  6. fn parse_config(args: &[String]) -> (&str, &str) {
  7. let filename = &args[1];
  8. let query = &args[2];
  9. (filename, query)
  10. }

3.2 组合配置值

  1. struct Config {
  2. filename: String,
  3. query: String,
  4. }
  5. pub fn main() {
  6. let args: Vec<String> = env::args().collect();
  7. let config = parse_config(&args);
  8. …………
  9. }
  10. fn parse_config(args: &[String]) -> Config {
  11. // 这里如果不使用clone,则涉及较复杂的生命周期,对于简单的程序,将得不偿失
  12. let filename = args[1].clone();
  13. let query = args[2].clone();
  14. Config {
  15. filename: filename,
  16. query: query,
  17. }
  18. }

3.3 为配置增加构造函数

  1. struct Config {
  2. filename: String,
  3. query: String,
  4. }
  5. pub fn main() {
  6. let args: Vec<String> = env::args().collect();
  7. let config = Config::new(&args);
  8. …………
  9. }
  10. impl Config {
  11. fn new(args: &[String]) -> Config {
  12. let filename = args[1].clone();
  13. let query = args[2].clone();
  14. Config {
  15. filename: filename,
  16. query: query,
  17. }
  18. }
  19. }

3.4 改进错误处理

  1. struct Config {
  2. filename: String,
  3. query: String,
  4. }
  5. pub fn main() {
  6. let args: Vec<String> = env::args().collect();
  7. // 调用 Result类型的 unwrap_or_else() 方法
  8. let config = Config::new(&args).unwrap_or_else(|err|{
  9. println!("解析参数失败: {}",err);
  10. process::exit(1);
  11. });
  12. …………………………
  13. }
  14. impl Config {
  15. // 返回值变成了 Result 类型
  16. fn new(args: &[String]) -> Result<Config,&'static str>{
  17. if args.len() < 3 {
  18. return Err("参数太少")// 这里返回Err
  19. }
  20. let filename = args[1].clone();
  21. let query = args[2].clone();
  22. // 这里返回 Result 类型
  23. Ok(Config {
  24. filename: filename,
  25. query: query,
  26. })
  27. }
  28. }

3.5 从main中提取逻辑

  1. pub fn main() {
  2. let args: Vec<String> = env::args().collect();
  3. // 调用 Result类型的 unwrap_or_else() 方法
  4. let config = Config::new(&args).unwrap_or_else(|err|{
  5. println!("解析参数失败: {}",err);
  6. process::exit(1);
  7. });
  8. // 注意 if let 的使用
  9. if let Err(err) = run(config){
  10. println!("读取文件失败: {}",err);
  11. process::exit(2);
  12. }
  13. }
  14. fn run(config : Config) -> Result<(),Box<Error>>{
  15. let mut content = String::new();
  16. // 注意 ? 运算符的使用
  17. File::open(config.filename)?.read_to_string(&mut content)?;
  18. println!("With text:\n{}", content);
  19. Ok(())
  20. }

4 将代码拆分到crate

  1. // 这是 main.rs
  2. use std::env as vne;
  3. use std::process;
  4. extern crate chap12;
  5. fn main(){
  6. section3_step7();
  7. }
  8. fn section3_step7() {
  9. let args: Vec<String> = vne::args().collect();
  10. println!("{:?}", args);
  11. // unwrap_or_else
  12. let config = chap12::Config::new(&args).unwrap_or_else(|err|{
  13. eprintln!("解析参数失败: {}",err);
  14. process::exit(1);
  15. });
  16. println!("文件: {} 查找: {}",config.filename,config.query);
  17. // if let 的使用
  18. if let Err(err) = chap12::run(config){
  19. eprintln!("读取文件失败: {}",err);
  20. process::exit(2);
  21. }
  22. }
  1. // 这是 lib.rs
  2. use std::fs::File;
  3. use std::io::prelude::*;
  4. use std::error::Error;
  5. pub struct Config {
  6. pub filename: String,
  7. pub query: String,
  8. pub case_insensitive: bool,
  9. }
  10. pub fn run(config : Config) -> Result<(),Box<Error>>{
  11. let mut content = String::new();
  12. // ? 运算符的使用
  13. let mut file = File::open(config.filename)?.read_to_string(&mut content)?;
  14. println!("With text:\n{}", content);
  15. // if 表达式
  16. let result = if config.case_insensitive{
  17. search(&config.query,&content)
  18. }else{
  19. search_insensitive(&config.query,&content)
  20. };
  21. println!("结果: {:?}",result);
  22. Ok(())
  23. }
  24. impl Config {
  25. pub fn new(args: &[String]) -> Result<Config,&'static str>{
  26. if args.len() < 3 {
  27. return Err("参数太少")
  28. }
  29. let filename = args[1].clone();
  30. let query = args[2].clone();
  31. Ok(Config {
  32. filename: filename,
  33. query: query,
  34. case_insensitive: std::env::var("CASE_INSENSITIVE").is_err(),
  35. })
  36. }
  37. }
  38. pub fn search<'a>(query:&str,content:&'a str) -> Vec<&'a str>{
  39. let mut result = Vec::new();
  40. for line in content.lines(){
  41. if line.contains(query){
  42. result.push(line);
  43. }
  44. }
  45. result
  46. }
  47. pub fn search_insensitive<'a>(query:&str,content:&'a str) -> Vec<&'a str>{
  48. let mut result = Vec::new();
  49. let query = query.to_lowercase();
  50. for line in content.lines(){
  51. if line.to_lowercase().contains(&query){
  52. result.push(line);
  53. }
  54. }
  55. result
  56. }