この記事では、私がRustで書き捨てコードを書く際にやっている
Rustプロジェクトの構成について紹介します。
Rustでは、cargo new
すれば簡単にプロジェクト作成することはできます。
だた、毎回ちょっとした処理を書くために、Cargo.tomlから設定を書いていくのも面倒でしょう。
しかも、私はRustを書く時にCLionを使っているので、毎回プロジェクトディレクトリに登録するのも億劫になります。
書き捨てコードとは
私が言っている書き捨てコードとは、
ちょっとした処理を実装する際に使うスクリプト的なものです。
例えば、MySQLなどのデーターベースから取得したデータを、何かしらの形に加工して、 CSVなどに出力したいとか。 複数のJsonファイルを読み込んで、特定条件で突き合わせて抽出したりなど。
何かしらの入力ファイルがあって、処理を行った後、成形して出力したい。
みたいなことが大半です。
あとは、ちょっとした処理を実験したいときにも書き捨てコードを作ります。
書き捨てコードとは、 一時的な利用を目的としたコードです。
examples ディレクトリを使う
Rustで書き捨てコードを書くときに、
毎回、cargo new
でプロジェクトディレクトリを作成するよりも、
私がオススメするのは、examplesディレクトリを活用して管理していくことです。
examplesというディレクトリには、rsファイルを配置する事ができて、
それぞれ、exampleごとに個別に実行する事ができます。
examplesディレクトリを使った構成例
上図の赤線をひいたファイルは、全てexampleとして個別に実行する事が可能になります。
cargo run —example <Example名>
や、
cargo test —example <Example名>
などで実行とテストを個別で動かすことができます。
私は基本、YYYYMMDD_xxxxx みたいに日付のディレクトを作って、
そこに main.rsファイルを配置するというスタイルでやっています。
他にも、src/bin 配下にサブディレクトリを作るという方法や、benches, tests ディレクトリを使うという手もありますが、 examplesディレクトリを使うのが、このようなケースでは一番相性が良いと思います。
src/bin配下は、ターゲットビルド時に、サブディレクトリ毎にバイナリを生成しようとするので、
ファイル数が増えてくるとコンパイル時間にとても時間がかかってしまいます。
benchesと、tests ディレクトリは、ちょっと用途が違いすぎるのと、 CLionなどのIDE上で実行すると、コンソール出力がテスト用に収集されてしまうというのもあって、不便だったりします。 (これは設定で変えれますが)
example run で実行する
examplesディレクトリ配下のコードは、
cargo run
, cargo test
で個別に実行する事ができます。
実行する際には、cargo run —example xxxxx
と、example名を指定する必要があります。
一度、cargo run —example
とだけ実行すると、指定できるexample名を出力してくれます。
テストコードの方は、
cargo test —example <Example名>
と同様に個別に実行指定できますが、
cargo test —examples
で全てのexamples を指定してテスト実行する事もできます。
ちなみに単純に、cargo test
とした場合、
examples配下のテスト項目は実行されませんが、
コンパイル対象にはなっているので、コードエラーがあれば検出します。
data, out ディレクトリ
ディレクトリ名はなんでもいいんですが、
私は書き捨てコード用の、入力ファイルと出力ファイル専用のディレクトリを、
各exampleディレクトリ配下につくるようにしています。
またこれらは、gitにコミットする必要ないファイルという事がおおいので、
.gitignore
で無視するようにしています。
.gitignore
/examples/*/data
/examples/*/out
このような設定をしておくと、
下図の赤枠内のファイルは、git管理ファイルとしては無視してくれます。
カレントディレクトリの設定
実行する際のカレントディレクトリは、各exampleディレクトリ直下であることの方が、都合がよいので、 書き捨てコードの冒頭では、下記のような set_directory関数をコールしておくと便利です。
// カレントディレクトリをソースファイルの親ディレクトリに設定
set_current_dir(Path::new(file!()).parent().unwrap()).unwrap();
上記だと、プロジェクトルート以外の場所から実行したときに、エラーになるので CARGO_MANIFEST_DIR環境変数を利用した方法も記載しておきます。
// カレントディレクトリをソースファイルの親ディレクトリに設定
// (プロジェクトルート以外の場所から実行しても、対応できるようにCARGO_MANIFEST_DIR環境変数を利用)
set_current_dir(
PathBuf::from_iter([env!("CARGO_MANIFEST_DIR"), file!()]).parent().unwrap(),
).unwrap();
file!
マクロは、プロジェクトルートを基準とした相対パスを返すため、
プロジェクトルートのPathを取得できる CARGO_MANIFEST_DIR環境変数を利用しています。
まとめ
- examples配下には、子ディレクトリ/main.rs の形で、いくつも作成することができる。
- examples配下のコードは、
cargo <run|test> —example <example>
でそれぞれ、個別に実行が可能。 - examples配下であればtargetビルドの負荷にならない。(src/binと比べて)
- 書き捨てだけど、日付ディレクトリで管理できるので、再利用がしやすい。
- 1元管理できるので、前も使ったなという部分は、lib配下にまとめ易くなる。