項目22:可視範囲を最小化しよう
デフォルトでモジュールの各アイテム(型やメソッド、定数)は private であり、同じモジュールやサブモジュールからしかアクセスできない
他からアクセスされるアイテムには pub を付加して public にする必要がある
ただし、pub を付けても自動的にその中身が public になるわけではない
code:rs
// モジュールに pub を付与しても、型や関数は public にならない
pub mod somemodule {
// 構造体に pub を付与しても、フィールドは public にならない
pub struct AStruct {
// フィールドには個別に pub を付ける必要がある
count: i32,
pub name: String,
}
// メソッドにも個別に pub を付ける必要がある
impl AStruct {
fn canonical_name(&self) -> String {
self.name.to_lowercase()
}
pub fn id(&self) -> String {
format!("{}-{}", self.canonical_name(), self.count)
}
}
}
また、pub は 付与されたアイテムが含まれるモジュールを見ることができるすべてのコード に対して、そのアイテムが見れるようにする
そのため、somecrate::somemodule が private であれば、その中で pub を用いても見えない
例外
enum を public にすると、バリアント もすべて public になる code:rs
pub enum AnEnum {
VariantOne,
VariantTwo(u32),
// バリアントのフィールドも public になる
VariantThree { name: String, value: String },
}
トレイト を public にすると、メソッドも public になる code:rs
pub trait DoSomething {
fn do_something(&self, arg: i32);
}
pub にはより細かく指定する方法がいくつかある
pub(crate): クレート 内のすべてのコードからのみ public に 用途: クレート内全体で用いるが、外部には公開したくないヘルパ関数など
pub(super): 親モジュールとサブモジュールからのみ public に
用途: 深いモジュールを持つクレートで、あるモジュールを特定の範囲でのみ使えるようにしたい場合
pub(in <path>): <path> 内のコードからのみ public に
<path> は、そのアイテムを含むモジュールの先祖モジュールを指す必要がある
用途: ソースコードを整理するため
∵ 公開される API からはアクセスできない機能の一部を、サブモジュールに移すことが可能になる
e.g. 標準ライブラリ
すべてのイテレータアダプタを内部サブモジュールである std::iter::adapters に集約し、以下のように指定している
サブモジュール内のすべての必要なアダプタメソッドの可視性に pub(in crate::iter) を指定する
e.g. std::iteradapters::map::Map::new など
外側の std::Iter では、すべての adapters:: 型を pub use(再エクスポート)する
pub(self): private(デフォルトと同じ)
pub(in self) と等価
用途: マクロ などで特別扱いを減らすため(生成されたコードの可視性を現在のモジュール内に限定する) private にも関わらず使用されていないアイテムがあると、コンパイラ は警告を出す code:_
warning: function inacccesible_fn is never used
--> src/main.rs:32:8
|
| fn inacccesible_fn(x: i32) -> i32 {
| ^^^^^^^^^^^^^^^
少なくとも他者が利用したり、再利用する可能性がある場合、可視性は「可能な限り少なく」するのが良い
このアドバイスは、Rust だけに限った話ではない
理由
1. 一度 public にすると、private にするのが難しいため
クレートを使っているコードに 破壊的変更 を与えてしまう 逆に private を public にしても、影響を与えない
2. 後の選択の幅を狭めないため
public の数が多いほど、互換性 を壊すことなく変更するのが難しくなる e.g.
データ構造体の内部実装を public にしているケース
より効率的なアルゴリズムを採用しようとすると、破壊的変更 が必要になる ヘルパ関数を public にしているケース
ユーザがその関数の動作の細部に依存したコードを書いてしまう
これはライブラリで特に問題となる
しかし、一時的な解決策がそのまま使われ続けることは多い ため、最初から良い習慣を身につけるのが良い