RustとTauriでGUI付きDockerのステータスを表示したりするアプリを作ってみてる件、ちまちまと検証しながら進めているのですが、 main.rs が膨らんでくるとやっぱり「あぁDocker関連のコードを分離したい」となってきます。 そういう時にどうすればいいのか、ちょっとやってみてました。キットもっと楽な方法があるはずなのですが、今の私の知見です。

単純に切り出す

  • 切り出す
  • mod 指示でインポートする

例えば元コードがこんな感じ。

1
2
3
4
5
6
7
fn hoge() -> String {
    "hogehoge".to_string()
}

fn main() {
    println!("{}", hoge());
}

ここから hoge() を切り出したければ、まずは単純に切り出してみます。 hoge.rsを作成してお引っ越し

1
2
3
4
// hoge.rs
fn hoge() -> String {
    "hogehoge".to_string()
}

これをmain.rsから読み込ませてみるのは、 mod キーワードでファイル名を渡してモジュールとして認識させることができます。

1
2
3
4
5
mod hoge; // hoge.rsを取り込む(モジュールとして)

fn main() {
    println!("{}", hoge::hoge());
}

でもこれはエラーになります。

❯ cargo check
    Checking libkiridashi v0.1.0 (/private/tmp/libkiridashi)
error[E0603]: function `hoge` is private
 --> src/main.rs:5:26
  |
5 |     println!("{}", hoge::hoge());
  |                          ^^^^ private function
  |
note: the function `hoge` is defined here
 --> src/hoge.rs:1:1
  |
1 | fn hoge() -> String {
  | ^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0603`.

さすがRust、エラーの詳しさが光ってます。

実はRustのモジュールやライブラリなど、切り出した場合の共有度合いはプライベート(private)だったりします。 「見せられないよ!」です。

ということで、晒してかまわない子はpubで見せてあげましょう。

1
2
3
4
// hoge.rs
pub fn hoge() -> String { // pub付けて公開
    "hogehoge".to_string()
}

これで動きます。 この方法でやるのが単一アプリの中での切り出しとしてはやりやすいですね。

ライブラリクレート

バイナリクレートではなくライブラリクレートとして切り出すことで、より分離度が増して良い感じになると思います。 ただこうなると再利用性とかまで考慮しないといけなくなるのでいくぶん面倒かもしれません。

バイナリクレートではありますが、トップディレクトリでライブラリクレートを作って入れ子にしちゃいます。

1
2
3
4
5
6
% cd /tmp/libkiridashi # 酷い名前だ
% ls -l
total 16
-rw-r--r--@ 1 densuke  wheel  156  4 14 06:13 Cargo.lock
-rw-r--r--@ 1 densuke  wheel  182  4 14 06:13 Cargo.toml
drwxr-xr-x@ 4 densuke  wheel  128  4 14 05:55 src

こんな所ですが作ってしまいます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
% cargo new --lib kiri
     Created library `kiri` package
% ls -l # トップディレクトリ
total 16
-rw-r--r--@ 1 densuke  wheel  156  4 14 06:13 Cargo.lock
-rw-r--r--@ 1 densuke  wheel  182  4 14 06:13 Cargo.toml
drwxr-xr-x@ 4 densuke  wheel  128  4 14 06:14 kiri
drwxr-xr-x@ 4 densuke  wheel  128  4 14 05:55 src
% ls -l kiri # kiri(lib)クレート内
total 8
-rw-r--r--@ 1 densuke  wheel  173  4 14 06:14 Cargo.toml
drwxr-xr-x@ 3 densuke  wheel   96  4 14 06:14 src

で、トップ側のCargo.tomlにkiriを足します。

1
2
[dependencies]
kiri = { path = "./kiri" }

これぐらい検出して自動的に入れてくれて良いんじゃない? と思ったりもします。 自動はないみたいですが、cargo addはできるみたい。

1
2
% cargo add kiri --path ./kiri
      Adding kiri (local) to dependencies.

あとは kiri/src/lib.rs にごりごりっと書いていきます。

1
2
3
pub fn rev(text: &str) -> String { // 先の知見でpub必要だよね
    text.chars().rev().collect()
}

&strな文字列を受け取って分解し、反転してStringにするという良くあるようなコードです。 こちらは依存関係に書き込んでいるので、 use で対応しちゃいましょう、と思いましたが、別に書かなくても、裏で勝手にuse宣言相当をしてくれるので書かなくてもOKみたいです。

1
2
3
4
5
6
7
mod hoge;


fn main() {
    println!("{}", hoge::hoge());
    println!("{}", kiri::rev("ふがほげ"));
}

これで両方とも動いてくれます。

1
2
3
4
5
6
7
% cargo run
   Compiling kiri v0.1.0 (/private/tmp/libkiridashi/kiri)
   Compiling libkiridashi v0.1.0 (/private/tmp/libkiridashi)
    Finished dev [unoptimized + debuginfo] target(s) in 1.29s
     Running `target/debug/libkiridashi`
hogehoge
げほがふ

これで少しはコードの分離性が確保できそうです、わーい。