// dictionary
pub.
pubは、項目(item)や構造体のフィールドを、定義したモジュールの外からも使えるようにするキーワードです。Rustでは既定ですべてが非公開で、非公開のものにアクセスできるのは定義したモジュールとその子孫だけです1。
pubが開けるのは常に1段だけです。モジュールをpubにしても中の項目は非公開のままで、構造体をpubにしてもフィールドは非公開のままなので、公開したいものに1つずつ付けていきます。例外は列挙型で、pubを付けるとバリアントもすべて公開されます2。
mod shop {
// モジュールを`pub`にしても、中身は別途`pub`を付けないと公開されない
pub mod cart {
// 構造体は`pub`でもフィールドは非公開のまま。公開したいものだけ指定する
pub struct Receipt {
pub total: u32, // 外から読める
issuer: String, // 非公開のまま
}
// 列挙型は`pub`を付けるとバリアントもすべて公開される
pub enum Payment {
Cash,
Card,
}
impl Receipt {
// 非公開のフィールドは、公開したメソッド経由でだけ外に見せられる
pub fn issuer(&self) -> &str {
&self.issuer
}
}
pub fn checkout(prices: &[u32], payment: Payment) -> Receipt {
let issuer = match payment {
Payment::Cash => "レジ",
Payment::Card => "カード端末",
};
Receipt {
total: prices.iter().sum(),
issuer: issuer.to_string(),
}
}
}
}
fn main() {
let card = shop::cart::checkout(&[980, 1250], shop::cart::Payment::Card);
let cash = shop::cart::checkout(&[300], shop::cart::Payment::Cash);
println!("{}円(発行: {})", card.total, card.issuer());
println!("{}円(発行: {})", cash.total, cash.issuer());
}Playgroundで開く対象ごとの既定
pubを付ける対象 |
公開されるもの | 中身の既定 |
|---|---|---|
| モジュール | モジュールの名前 | 中の項目は非公開(1つずつpub) |
| 構造体 | 型そのもの | フィールドは非公開 |
| 列挙型 | 型そのもの | バリアントはすべて公開 |
| トレイト | トレイトそのもの | 関連項目はすべて公開 |
公開が中身まで伝わるのは下2つ、つまりpubなトレイトの関連項目とpubな列挙型のバリアントだけです1。
外から使えるかどうかはパス(Path)のセグメントごとに確認されるため、途中のモジュールを公開しても、最後の関数にpubを付け忘れればアクセスできません。
補足
pubの付け忘れはE0603になる
モジュールだけを公開して中の項目にpubを付け忘れると、パスは書けてもアクセスできずコンパイルエラーになります。
mod shop {
pub mod cart {
// `pub`を付け忘れた
fn total(prices: &[u32]) -> u32 {
prices.iter().sum()
}
}
}
fn main() {
// エラー: E0603(cartは公開だが、totalは非公開)
println!("合計: {}円", shop::cart::total(&[980, 1250]));
}
公開範囲を絞る pub(crate) と pub(super)
pubのあとに丸括弧で範囲を書くと、「どこまでに見せるか」を絞れます1。
| 書き方 | 見せる範囲 |
|---|---|
pub |
制限なし(外部クレートからも) |
pub(crate) |
同じクレートの中 |
pub(super) |
親モジュール(pub(in super)と同じ) |
pub(self) |
現在のモジュール(pubなしと同じ) |
pub(in crate::shop) |
指定した祖先モジュール |
指定できるのはその項目の祖先モジュールだけで、無関係なモジュールにだけ見せることはできません。パスはcrate・self・superのいずれかで始める必要があります(2018エディション以降)1。
またこれらは「最大でここまで」という上限の指定にすぎず、実際に見えるかどうかは祖先モジュール側の可視性にも左右されます1。非公開モジュールの中のpub(crate)な項目は、クレート内からでも届きません。
なおpub(super)のsuperは可視性の指定であり、パスの先頭に書くsuperとは文法上の位置が異なります。
列挙型のバリアントだけ既定で公開される理由
The Rust Programming Languageは、列挙型は「バリアントが公開されていなければあまり役に立たない」ため、毎回すべてのバリアントにpubを書かせるのは煩わしく、既定を公開にしたと説明しています2。
逆に構造体は、フィールドを隠したまま使えることに意味があります。上のコード例のReceiptのように非公開フィールドを持つ構造体は、外からReceipt { .. }と書いて作れないため、公開した関連関数やメソッドを入口にする設計になります2。
公開APIの形は内部構造と切り離せる
pub useで項目を再エクスポートすると、内部のモジュール構造をそのまま外に見せずに済みます。非公開の項目を再エクスポートした場合は、通常の階層をたどる代わりに、その再エクスポートを経由して「プライバシーの連鎖が短絡される」と考えられます1。
Footnotes
この辞書が使われているページ
backlinks 8