emahiro/b.log

Drastically Repeat Yourself !!!!

Rust で grep コマンドを実装する

Overview

以下のハンズオンの内容にある Rust で grep コマンドを再実装する部分を再度自分でやってみました。

speakerdeck.com

やった内容は以下にまとめてあります。

github.com

Memo

いくつか工夫したりメモしたりしたポイントがあるのでまとめます。

検出した文字列が何行目にあるのか?を追加

grep コマンドなのでファイルの "何行目に" あるのかを表示したいと思って行数を表示する変更を追加しました => https://github.com/emahiro/il/pull/32/commits/3faac486add2181287a8ed5321cb95581e2a62b8

structopt について

github.com

このクレートを追加して struct の引数にマクロを定義すると以下のように簡単にコマンドラインオプションをデフォルトで実装してくれます(めちゃくちゃ便利...!!!)

use structopt::StructOpt;

#[derive(StructOpt)]
#[structopt(name = "rsgrep")]
struct GrepArgs {
    #[structopt(name = "PATTERN")]
    pattern: String,
    #[structopt(name = "FILEPATH")]
    files: Vec<String>,
}

実行サンプル

cargo run # 引数なしで実行する

cargo run
   Compiling rs_grep v0.1.0 ($PATH/src/github.com/emahiro/il/rs_sandbox/rs_grep)
    Finished dev [unoptimized + debuginfo] target(s) in 1.68s
     Running `target/debug/rs_grep`

# 以下が自動的に付与される。
error: The following required arguments were not provided:
    <PATTERN>

USAGE:
    rs_grep <PATTERN> [FILEPATH]...

For more information try --help

ちょっとマクロを読むのは慣れないと難しいな、という感想はありますが。

クレートについて

Rust のパッケージやライブラリ、モジュールについてはまだ全然理解できてないですが、Go の package 単位で依存関係を追加するのは クレートを追加する というようです。

パッケージとクレートの関係については TRPL に記載してました。

doc.rust-jp.rs

Cargo.toml の中身を見ても、src/main.rs については何も書いてありません。これは、Cargoは src/main.rs が、パッケージと同じ名前を持つバイナリクレートのクレートルートであるという慣習に従っているためです。

どんなプロジェクトでもそれ自体はクレートなんですね。

ちなみにクレート (Crate) は木箱という意味なんですね。なるほど。

ja.wikipedia.org

TRPL のクレートとパッケージの章をざっと見ててとりあえず以下のところを最初は覚えておくととっつきやすいのかなと思いました。

doc.rust-jp.rs

ビルドした成果物の格納場所

Cargo.toml があるディレクトリルートの target/debug 配下にバイナリファイルとしておいてあります。

./Cargo.toml
./target/debug/$BuildResult 

サンプルとして rust で grep を実装したコマンドは以下のように表示されます。

./target/debug/rs_grep
error: The following required arguments were not provided:
    <PATTERN>

USAGE:
    rs_grep <PATTERN> [FILEPATH]...

For more information try --help

rayon を利用して並行処理

https://github.com/rayon-rs/rayon を利用してファイルの読み込みを非同期に動かすようにしましたが、その際に関数型っぽく書く書き方に変更しました。
ref: https://github.com/emahiro/il/pull/32/commits/d97d35a14b0f2f55b7100d5e85c41d04f539e5cb

これには理由があって、元々手続き型っぽく書いていたんですが、rayon のクレートを追加した際に変更した iter() -> par_iter() の部分で返り値が Iterator 型から String の Vector 型に変更されていたので Iteration を回す for ... in ... 構文が使えなくなりました。
そのため関数型っぽく for_reach の中でラムダ式を書いて同じような実装を実現してます。

See Also

doc.rust-jp.rs