Rust 學習筆記:使用迭代器改進 minigrep
- Rust 學習筆記:使用迭代器改進 minigrep
- 不使用 clone,而使用迭代器
- 使用迭代器適配器使代碼更清晰
- 在循環或迭代器之間進行選擇
Rust 學習筆記:使用迭代器改進 minigrep
前情提要:https://blog.csdn.net/ProgramNovice/article/details/148192426
有了這些關于迭代器的新知識,我們可以通過使用迭代器使代碼更清晰、更簡潔。讓我們看看迭代器如何改進 Config::build 函數和 search 函數的實現。
不使用 clone,而使用迭代器
原函數:
impl Config {pub fn build(args: &[String]) -> Result<Config, &'static str> {if args.len() < 3 {return Err("not enough arguments");}let query = args[1].clone();let file_path = args[2].clone();let ignore_case = env::var("IGNORE_CASE").is_ok();Ok(Config { query, file_path, ignore_case })}
}
有了關于迭代器的新知識,我們可以修改構建函數,使其接受迭代器的所有權作為參數,將 String 值從迭代器移到 Config 中,而不是調用 clone 并進行新的分配。
env::args 函數返回一個迭代器,類型是 std::env::Args,該類型實現了 Iterator trait 并返回 String 值。
修改代碼,使得其所有權直接傳遞給 Config::build 函數。
fn main() {let config = Config::build(env::args()).unwrap_or_else(|err| {eprintln!("Problem parsing arguments: {err}");process::exit(1);});// --skip--
}
接下來,我們需要更新 Config::build 函數:
impl Config {pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {args.next();let query = match args.next() {Some(arg) => arg,None => return Err("Didn't get a query string"),};let file_path = match args.next() {Some(arg) => arg,None => return Err("Didn't get a file path"),};let ignore_case = env::var("IGNORE_CASE").is_ok();Ok(Config { query, file_path, ignore_case })}
}
我們更新了 Config::build 函數的簽名,所以參數 args 有一個泛型類型,trait 約束為 impl Iterator<Item = String> 而不是 &[String],這意味著 args 可以是任何實現 Iterator trait 并返回 String 項的類型。因為我們獲得了 args 的所有權我們將通過迭代來改變 args,我們可以在 args 參數的說明中添加 mut 關鍵字來使它可變。
使用迭代器適配器使代碼更清晰
項目的 search 函數中利用了迭代器:
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<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {contents.lines().filter(|line| line.contains(query)).collect()
}
函數式編程風格傾向于最小化可變狀態的數量,以使代碼更清晰。刪除可變狀態可能會使將來的增強實現并行搜索,因為我們不必管理對結果向量的并發訪問。
類似的,我們修改 search_case_insensitive 函數:
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {contents.lines().filter(|line| line.to_lowercase().contains(&query.to_lowercase())).collect()
}
在循環或迭代器之間進行選擇
大多數 Rust 程序員更喜歡使用迭代器風格。一開始很難掌握竅門,但是一旦了解了各種迭代器適配器及其功能,就會更容易理解迭代器。
代碼沒有處理循環的各個部分和構建新的向量,而是專注于循環的高級目標。這抽象掉了一些常見的代碼,因此更容易看到這些代碼特有的概念,例如迭代器中的每個元素必須通過的過濾條件。