案例:一个简单的工具

117 阅读2分钟

案例:一个简单的工具

  • 首先,通过命令创建一个空白项目

    cargo new minigrep
    
  • 然后,在这个项目下运行命令检验环境是否正确

    cargo run
    

    如果打印出了 hello world!,则表明环境无误

  • 下面是这个案例的完整代码,但最好是按 官方教程 走一遍,相信你会获益良多

    • poem.txt

      I'm nobody! Who are you?
      Are you nobody, too?
      Then there's a pair of us - don't tell!
      They'd banish us, you know.
      
      How dreary to be somebody!
      How public, like a frog
      To tell your name the livelong day
      To an admiring bog!
      
    • Cargo.toml

      • 在初始化的基础上,增加如下配置

        [[bin]]
        name = "minigrep"
        path = "src/main.rs"
        
        [lib]
        name = "minigrep"
        path = "src/lib.rs"
        
    • src/main.rs

      use std::{env, process};
      
      use minigrep::Config;
      
      fn main() {
          // * 第一步:获取用户输入的参数
          // 注意:env::args() 的任何参数包含无效 Unicode 字符时会 panic
          // 扩展:如果要处理无效 Unicode 字符,可以使用 std::env::args_os().collection(),来获取 OsString 的集合
          let args: Vec<String> = env::args().collect();
          let conf = Config::new(&args).unwrap_or_else(|err| {
              println!("{}", err);
              process::exit(1)
          });
      
          // * 第二步:执行期望的逻辑
          if let Err(e) = minigrep::run(conf) {
              println!("{}", e);
              process::exit(1);
          }
      }
      
      
    • src/lib.rs

      use std::{env, error::Error, fs};
      
      pub struct Config {
          pub query: String,
          pub filename: String,
          pub case_sensitive: bool,
      }
      
      impl Config {
          pub fn new(args: &[String]) -> Result<Self, &'static str> {
              // 处理环境变量,这个环境变量是在当前命令行窗口里通过 $env:var_name=var_value 设置的
              let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
              return match args.len() {
                  0 => Err("系统错误"),
                  1 => Err("请输入想要查找的单词"),
                  2 => Err("请输入查找的文件"),
                  // 只要长度够长就行,后面的参数直接忽略
                  _ => Ok(Config {
                      case_sensitive,
                      query: args[1].clone(),
                      filename: args[2].clone(),
                  }),
              };
          }
      }
      
      // 完全匹配的查找
      pub fn search<'a>(query: &'a str, file_content: &'a str) -> Vec<&'a str> {
          let mut result = Vec::new();
      
          // 遍历每一行,查找每一行里是否包含查找的词组
          for current_line in file_content.lines() {
              if current_line.contains(query) {
                  result.push(current_line);
              }
          }
      
          return result;
      }
      
      // 忽略大小写的查找
      pub fn search_case_insensitive<'a>(query: &'a str, file_content: &'a str) -> Vec<&'a str> {
          // 将查找的词组转为小写
          let query_lower = query.to_lowercase();
          let mut result = Vec::new();
      
          // 遍历每一行
          for current_line in file_content.lines() {
              // 将每一行都转为小写,然后再查找是否包含查找的词组的小写
              if current_line.to_lowercase().contains(&query_lower) {
                  result.push(current_line);
              }
          }
      
          return result;
      }
      
      /// Result<(), Box<dyn Error>> 意味着函数会返回实现了 Error trait 的类型
      /// dyn 即 dynamic
      pub fn run(conf: Config) -> Result<(), Box<dyn Error>> {
          let file_content = fs::read_to_string(&conf.filename)?;
          let result = if conf.case_sensitive {
              search(&conf.query, &file_content)
          } else {
              search_case_insensitive(&conf.query, &file_content)
          };
      
          if result.len() == 0 {
              println!(
                  "文件 {} 中未查询到包含 {} 的行...",
                  conf.filename, conf.query
              );
          } else {
              for current_line in result {
                  println!("{}", current_line);
              }
          }
      
          return Ok(());
      }
      
      #[cfg(test)]
      mod tests {
          use super::*;
      
          // 测试大小写敏感的查找方法
          #[test]
          fn case_sensitive() {
              let query = "duct";
              // 下面顶齐左侧,是为了避免有空格
              let file_content = "\n
      Rust:
      safe, fast, productive.
      Pick three.";
              assert_eq!(vec!["safe, fast, productive."], search(query, file_content));
          }
      
          // 测试大小写不敏感的查找方法
          #[test]
          fn case_insensitive() {
              let query = "rUsT";
              // 下面顶齐左侧,是为了避免有空格
              let file_content = "\n
      Rust:
      safe, fast, productive.
      Pick three.
      Trust me.";
              assert_eq!(
                  vec!["Rust:", "Trust me."],
                  search_case_insensitive(query, file_content)
              );
          }
      }