Progrust Library.

// 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) 指定した祖先モジュール

指定できるのはその項目の祖先モジュールだけで、無関係なモジュールにだけ見せることはできません。パスはcrateselfsuperのいずれかで始める必要があります(2018エディション以降)1

またこれらは「最大でここまで」という上限の指定にすぎず、実際に見えるかどうかは祖先モジュール側の可視性にも左右されます1。非公開モジュールの中のpub(crate)な項目は、クレート内からでも届きません。

なおpub(super)superは可視性の指定であり、パスの先頭に書くsuperとは文法上の位置が異なります。

列挙型のバリアントだけ既定で公開される理由

The Rust Programming Languageは、列挙型は「バリアントが公開されていなければあまり役に立たない」ため、毎回すべてのバリアントにpubを書かせるのは煩わしく、既定を公開にしたと説明しています2

逆に構造体は、フィールドを隠したまま使えることに意味があります。上のコード例のReceiptのように非公開フィールドを持つ構造体は、外からReceipt { .. }と書いて作れないため、公開した関連関数メソッドを入口にする設計になります2

公開APIの形は内部構造と切り離せる

pub useで項目を再エクスポートすると、内部のモジュール構造をそのまま外に見せずに済みます。非公開の項目を再エクスポートした場合は、通常の階層をたどる代わりに、その再エクスポートを経由して「プライバシーの連鎖が短絡される」と考えられます1

Footnotes

  1. Visibility and Privacy — The Rust Reference 2 3 4 5 6

  2. Exposing Paths with the pub Keyword / Making Structs and Enums Public — The Rust Programming Language 2 3

この辞書が使われているページ

backlinks 8

  1. 辞書super
  2. 辞書use宣言
  3. 辞書パス(Path)
  4. 辞書モジュール
  5. 辞書モジュールのファイル分割
  6. 辞書再エクスポート
  7. 動かして学ぶRustプログラミング問題集 › 第13章 モジュールと公開範囲
  8. 動かして学ぶRustプログラミング問題集 › 第16章 公開APIと外部パッケージ