Closed8
The Rust Programming Language - Chapter 7 メモ
肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する
- パッケージ: クレートをビルドし、テストし、共有することができるCargoの機能
- クレート: ライブラリか実行可能ファイルを生成する、木構造をしたモジュール群
- モジュール と use: これを使うことで、パスの構成、スコープ、公開するか否かを決定できます
- パス: 要素(例えば構造体や関数やモジュール)に名前をつける方法
パッケージとクレート
- クレート
- バイナリ or ライブラリのこと
- crate root:Rust コンパイラの開始点かつ、クレートのルートモジュールを作るソースファイル
- 関連した機能を1つのスコープにまとめることで、その機能が複数のプロジェクト間で共有しやすいようにする。名前の衝突を防ぐ
- 元々の意味:ものを運ぶ木わくまたは竹かご
- パッケージ
- ある機能群を提供する1つ以上のクレート
- Cargo.toml という、クレートをどのようにビルドするかを説明するファイルを持っている
- パッケージは 0 or 1個のライブラリクレートを持っていないといけない(2個以上はダメ)
cargo new
すると
- Cargo.toml
- src/main.rs
が生成される(パッケージが作られる)。Cargo.toml には src/main.ts への言及はないが、convention により src/main.rs が crate root として扱われる。
モジュールを定義して、スコープとプライバシーを制御する
- モジュール
- クレート内のコードをグループ化し、可読性と再利用性を上げる。
- 要素のプライバシーも制御できる。public / private
- モジュールはネストできる
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
- 暗黙的にルートには
crate
という名前のモジュールができる。 - ディレクトリツリーのようなイメージ
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
モジュールツリーの要素を示すためのパス
-
絶対パス:
crate
を使うことで、クレートルートからスタートする -
相対パス:
self
,super
あるいは今のモジュール内の識別子を使うことで、現在のモジュールからスタートする -
絶対パスも相対パスも、パス間はダブルコロン
::
で区切られる -
モジュールのプライバシー境界
- Rustは、内部実装の詳細を隠すことが標準。private by default
- 同じモジュール内で定義されている兄弟
-
pub
キーワードを使って要素を公開することができる。 - プライバシールールは、モジュール、構造体、enum、関数、メソッドに適用される
// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
pub fn eat_at_reastaurant() {
// 絶対パス
crate::front_of_house::hosting::add_to_waitlist();
// 相対パス
front_of_house::hosting::add_to_waitlist();
}
↑でのポイントは、front_of_house
は pub でないのに eat_at_reastaurant
からはアクセスできること。これは front_of_house
と eat_at_reastaurant
が同じモジュール内で定義されているから。
相対パスをsuperで始める
// crate モジュールに属している
fn serve_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
// crate::serve_order() と同義
// back_of_house から見て crate が super にあたる
super::serve_order();
}
fn cook_order() {}
}
構造体とenumを公開する
- 構造体自体に
pub
キーワードつけてもフィールドは非公開のまま。- フィールド個別に
pub
をつけるつけないを決める。 - 構造体はフィールドが公開されていなくても便利なことが多いので、pub がついていない限りは非公開のルールに従う。
- フィールド個別に
pub struct Breakfast {
pub toast: String, // 公開される
seasonal_fruit: String, // こちらは非公開のまま
}
- enum の場合は
pub
キーワードつけるとその variant は全て公開される。- enum は variant が公開されていないと不便。毎回全ての variant に pub つけるのは面倒なので、enum の variant はデフォルトで公開されるようになっている。
pub enum Appetizer {
Soup, // 公開される
Salad, // 公開される
}
useキーワードでパスをスコープに持ち込む
useキーワード(TypeScript の import のようなもの)
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// 絶対パス
use crate::front_of_house::hosting;
// 相対パス
// use self::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
慣例に従ったuseパスを作る
- 関数の場合
- どこで
add_to_waitlist
が定義されたのかが不明瞭なのでよくない。 - 関数の定義されているモジュールまでに留めておくことで、関数がローカルで定義されていないことを明らかにできる。
- どこで
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// Bad
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
}
// Good
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
- 構造体や enum の場合
- フルパスを指定するのが慣例的。
- はっきりした理由はなく、自然発生した慣習で、みんなこのやり方で慣れているというだけ。
// Bad
use std::collections;
fn main() {
let mut map = collections::HashMap::new();
map.insert(1, 2);
}
// Good
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
- 名前が衝突してしまう場合は、モジュールまでに留めておく
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
// --snip--
// (略)
}
fn function2() -> io::Result<()> {
// --snip--
// (略)
}
新しい名前をasキーワードで与える
as
キーワードでリネームできる。(TypeScript の as のようなもの)
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
}
fn function2() -> IoResult<()> {
// --snip--
}
pub useを使って名前を再公開する
pub use
で再公開する(TypeScript の re-export のようなもの)
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// re-export
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
外部のパッケージを使う
外部パッケージも Cargo.toml に追加しさえすれば、use キーワードを使って同様に使うことができるようになる。
巨大なuseのリストをネストしたパスを使って整理する
use std::cmp::Ordering;
use std::io;
// ↓
use std::{cmp::Ordering, io};
self
を使った方法
use std::io;
use std::io::Write;
↓
use std::io{self, Write};
glob演算子
wildcard 指定できる
use std::collections::*;
モジュールを複数のファイルに分割する
// src/lib.rs
// mod front_of_houseの後にブロックではなくセミコロンを使うと、Rustにモジュールの中身を`モジュールと同じ名前をした別のファイル`から読み込むように命令します。
// これは覚えておかないと
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
以下のファイルの中身が src/lib.rs に展開される
// src/front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {}
}
モジュールを複数のファイルに分割する
// src/lib.rs
// mod front_of_houseの後にブロックではなくセミコロンを使うと、Rustにモジュールの中身を`モジュールと同じ名前をした別のファイル`から読み込むように命令します。
// これは覚えておかないと
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
以下のファイルの中身が src/lib.rs に展開される
// src/front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {}
}
まとめ
- パッケージ
- Cargo.toml で管理される単位
- 0 or 1 個のクレートを持つ
- クレート
- バイナリ or ライブラリのこと
- 1個以上のモジュールを持つ
- モジュールはファイルシステムのような階層構造を持ち、モジュールツリーと呼ばれる
- ルートには
crate
と呼ばれるルートモジュールが必ず存在する- モジュール
- クレート内のコードをグループ化し、可読性と再利用性を上げるためのもの
- モジュールはデフォルトで private スコープ
- 公開したい場合は
pub
キーワードを使う - ちなみに、構造体自体を
pub
にしても、そのフィールドは別途pub
指定しないといけない - enum は
pub
にすると、variant 全て公開される
- 公開したい場合は
- モジュールのパスを指定するには 絶対パス(
crate
)、相対パス(self
)が使える- モジュールを import するには
use
キーワードを使う- 関数は、それが属するモジュールまでに留めておき、
モジュール::関数()
と呼び出すのが慣例的。 - 構造体や enum の場合は、フルパスを指定するのが慣例的。
- import したモジュールには
as
キーワードで別名を与えることができる。 -
pub use
の組み合わせで re-export ができる。 - 重複するパスはまとめて、差分のみを
{}
内に記述できる。 - wildcard で指定できる。
- 関数は、それが属するモジュールまでに留めておき、
- モジュールを import するには
- クレート
このスクラップは2021/07/10にクローズされました