可視性とカプセル化

モジュール内のアイテムと同様に、struct のフィールドもデフォルトでは非公開です。非公開フィールドも同様に、そのモジュールの他の部分(子モジュールを含む)からは参照できます。これにより、struct の実装詳細をカプセル化し、外部から見えるデータや機能を制御できます。

// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

use outer::Foo;

mod outer {
    pub struct Foo {
        pub val: i32,
        is_big: bool,
    }

    impl Foo {
        pub fn new(val: i32) -> Self {
            Self { val, is_big: val > 100 }
        }
    }

    pub mod inner {
        use super::Foo;

        pub fn print_foo(foo: &Foo) {
            println!("Is {} big? {}", foo.val, foo.is_big);
        }
    }
}

fn main() {
    let foo = Foo::new(42);
    println!("foo.val = {}", foo.val);
    // let foo = Foo { val: 42, is_big: true };

    outer::inner::print_foo(&foo);
    // println!("Is {} big? {}", foo.val, foo.is_big);
}
  • このスライドでは、struct における可視性がモジュール単位で決まることを示しています。オブジェクト指向言語に慣れた受講者は、型がカプセル化の境界であることに慣れているかもしれません。そのため、このスライドは Rust の振る舞いがそれとは異なることを示しつつ、それでもカプセル化を実現できることを示しています。

  • is_big フィールドが Foo によって完全に制御されている点に注目してください。これにより、Foo はその初期化方法を制御し、必要な不変条件(たとえば、val > 100 の場合にのみ is_bigtrue になること)を強制できます。

  • 型の非公開フィールドや非公開メソッドにアクセスするために、同じモジュール内(子モジュールを含む)でヘルパー関数を定義できることを指摘してください。

  • 最初のコメントアウトされた行は、非公開フィールドを持つ struct を初期化できないことを示しています。2 つ目は、非公開フィールドに直接アクセスすることもできないことを示しています。

  • enum では可視性を設定できません。バリアントと、そのバリアント内のデータは常に公開です。

さらに学ぶには

  • enum における可視性(あるいはその欠如)についてさらに知りたがる受講者がいれば、#[doc_hidden]#[non_exhaustive] を取り上げ、それらが enum でできることをどのように制限するために使われるかを示せます。

  • 他のモジュールに impl ブロックがある場合でも、モジュールの可視性は引き続き適用されます(playground の例)