やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのイテレータ(Minigrep改善)

 全体的に。

成果物

 以前の改善版。

参考

コード修正

型の変更

 Config構造体のnew関数にある引数の型を変更。(ベクタの参照→ベクタのイテレータ)   src/main.rs before

fn main() {
    let args: Vec<String> = env::args().collect();
    let config = Config::new(&args).unwrap_or_else(|err| {
    // --snip--
}

src/main.rs after

fn main() {
    let config = Config::new(env::args()).unwrap_or_else(|err| {
    // --snip--
}

 std::env::args()イテレータを実装するargs: std::env::Argsを返す。

src/lib.rs

impl Config {
    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {

 コマンド引数の所有権を奪ってargsを変更する予定なのでmutを付与する。

impl Config {
    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
        args.next(); // 最初の値(実行ファイルパス)を読み飛ばす
        let query = match args.next() {
            Some(arg) => arg,
            None => return Err("検索文字列が取得できません。第一引数に指定してください。"),
        };
        let filename = match args.next() {
            Some(arg) => arg,
            None => return Err("検索対象ファイルパスが取得できません。第二引数に指定してください。"),
        };
        let case_sensitive = std::env::var("CASE_SENSITIVE").is_ok(); // is_errでなく
        Ok(Config { query, filename, case_sensitive })
    }
}

イテレータ活用

search関数

src/lib.rs before

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
}

src/lib.rs after

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents.lines().filter(|line| line.contains(query)).collect()
}

search_case_insensitive関数

src/lib.rs before

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase();
    let mut results = Vec::new();
    for line in contents.lines() {
        if line.to_lowercase().contains(&query) { results.push(line); }
    }
    results
}

src/lib.rs after

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().as_str()))
        .collect()
}

 ただ、これだとcontainsに含まれる改行コードの数*2回もto_lowercaseが実行されてしまう。それぞれ関数の先頭で1回ずつ実行してキャッシュすればいいはず。そう思って以下のコードを書いたが、エラーになる。どうすれば解決できるか不明。

 ドキュメントは丸投げ。コードが書いてない。ここは丁寧に解説して欲しかった。

error[E0515]

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase();
    contents.to_lowercase().lines()
        .filter(|line| line.contains(&query))
        .collect()
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents.to_lowercase().lines()
        .filter(|line| line.contains(query.to_lowercase().as_str()))
        .collect()
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents.to_lowercase().as_str().lines()
        .filter(|line| line.contains(query.to_lowercase().as_str()))
        .collect()
}

 どれも以下エラー。

error[E0515]: cannot return value referencing temporary value

error[E0277]

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let contents = contents.to_lowercase();
    contents.lines()
        .filter(|line| line.contains(query.to_lowercase()))
        .collect()
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let contents = contents.to_lowercase();
    contents.lines()
        .filter(|line| line.contains(query.to_lowercase().as_str()))
        .collect()
}

 どれも以下エラー。

error[E0277]: expected a `std::ops::FnMut<(char,)>` closure, found `std::string::String`

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで