空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

Rust独学メモ

PackageとCrate

最初にRust入門した時には、cargo newして、コードを実装して、テストを書いて、何となく動いたわーい、って誰しもやると思う。でもある程度構造化されたプログラムを組もうとすると

ユーザ定義型・メソッド・関数 < モジュール階層 < クレート < パッケージ

という風に階層的に管理する必要が出てくる。モジュールの階層は、ディレクトリ構造に対応しているので分かりやすいが、クレートとパッケージはどのような関係にあるのだろう。また最初に cargo new して作るものが一体何なのか、少しわかりにくいのではないだろうか。The Bookによると

A crate is the smallest amount of code that the Rust compiler considers at a time.

クレートとはRustコンパイラが一度に考慮するコードの最小単位である ⇒ コンパイル単位であるということか。ライブラリ全体がコンパイル単位なので、forward declaration のようなことをしなくてよいのだろうと推測

A crate can come in one of two forms: a binary crate or a library crate.

クレートにはバイナリと、ライブラリ・クレートの2種類 ⇒ OK
でもって、バイナリには main 関数がある。まあいいでしょう。

The crate root is a source file that the Rust compiler starts from and makes up the root module of your crate

クレート・ルート、はソースファイルで、ルート・モジュールを構成する起点となる。(なんか新しい概念が2つ提示された。詳しくは後で、だと)

A package is a bundle of one or more crates(中略)A package contains a Cargo.toml file that describes how to build those crates.

パッケージは1つ以上のクレートを束ねたもの ⇒ OK。最初に提示した図の通り
パッケージは Cargo.toml ファイルを含む ⇒ なるほど、Cargo.toml を見たら、そこにパッケージがあると思えばよい

さらに、続いて

A package can contain as many binary crates as you like, but at most only one library crate.

パッケージ内には好きな数だけバイナリクレートを作ることができる。でもライブラリクレートは最大1個 ⇒ バンドルと言いつつライブラリは1つですか…。恣意的かつ大胆なデザイン

cargo new してできたフォルダには Cargo.toml があるので、cargo new で作られたものは、まごうことなくパッケージであると理解できた。それでは、クレートはどこにあるのだろうか?

src/main.rs is the crate root of a binary crate with the same name as the package. Likewise, Cargo knows that if the package directory contains src/lib.rs

src/main.rs が、パッケージと同名のバイナリクレートのクレート・ルート。src/lib.rs がパッケージと同名のライブラリクレートのクレート・ルート ⇒ このファイルの存在が、クレートの定義と同義。パッケージと同名のバイナリクレートを作りたかったら src/main.rs を置けば良くて、ライブラリクレートを作りたかったら、src/lib.rs を置くしかない(パッケージに作れるライブラリクレートは最大1個までだから)ということ。ライブラリを作成するにあたっては、パッケージとクレートは一対一対応している。

ところで、先ほど「パッケージ内には好きな数だけバイナリクレートを作ることができる」とあった、それを実現するにはどうすればよいのだろう?

A package can have multiple binary crates by placing files in the src/bin directory: each file will be a separate binary crate.

src/bin ディレクトリを作ってその下に、ファイルを作ればよい。それぞれのファイルが別々のバイナリクレートになる ⇒ さっき src/main.rs はクレート・ルートだと説明を受けたが、src/bin 以下のファイルはクレートになる。それはクレート・ルート?説明一貫してなくないか?

まとめ
  • cargo new では、パッケージと、パッケージと同名のクレートのクレート・ルートが作成される
  • ライブラリクレートを作りたかったら、クレート・ルートとなる src/lib.rs ファイルを置く
  • パッケージと同名のバイナリクレートを作りたかったら、クレート・ルートとなる src/main.rs ファイルを置いて main 関数を書く
  • それ以外のバイナリクレートを作りたかったら src/bin フォルダを置いて、任意の .rs ファイルを置くと、そのファイルひとつひとつが、バイナリクレートのクレート・ルートになる
ここが残念
  • クレートとは、プログラムの構造上は、プログラムの階層構造の中では、モジュールの上に来る名前空間であるのと同時に、Rustのコンパイル単位のことでもある。
    • モジュールの上に来る名前空間はパッケージで良くて、実際一対一対応しているので、冗長と言える
    • Rustのコンパイル単位であるかどうかということは、コンパイラの実装の詳細でプログラマには直接関係ないのでつまびらかにしなくてもよいのでは。多くのRustユーザにはあまり必要ない
  • crates.io で検索して cargo add で依存関係を付けているのはパッケージ(に一対一対応したクレート)。パッケージのことをクレートと呼んで、クレートはRustコンパイラの実装の中に留めていたほうが良かったのではないか
  • Cargo.toml には [workspace] という概念があって、パッケージの中にパッケージが作れる。「パッケージにライブラリクレートは最大1個」しか作れないが、パッケージは作れる。こんな詭弁が許されるのは、愛されているが故なのか。ここはもう少し掘り下げて調べてみることにする予定
  • モジュール化の根幹にある crate がコンパイル単位であり、ライブラリのことであり、実行ファイルでもあり、パッケージも含意している多義的で曖昧な用語になっている。語彙が乏しい人が作った残念なライブラリ臭がする。美しい世界ではない
  • 「クレートはコンパイル単位であり、パッケージはクレートのメタ情報を定義する場所」であることは、少なくとも言えそう。それ以外の説明は The Book を持ってしても的確では無いように思えてならない