やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのアクセス修飾子(pub)

 pubを付与すると公開(public)される。pubがなければ非公開(private)。

成果物

参照

pub

main.rs

fn main() {
    println!("{}", my_mod::public());
//    println!("{}", my_mod::private()); // error[E0603]: function `private` is private
}
mod my_mod {
    pub fn public() -> String { String::from("my_mod::public()") }
    fn private() -> String { String::from("my_mod::private()") }
}

 fnの前にpubがあると参照可。ないと不可。

プライバシー規則

 プライバシー規則によると以下。

  • 要素が公開(pub)なら、どの親モジュールを通してもアクセス可
  • 要素が非公開なら、直接の親モジュールとその親の子モジュールのみアクセス可

非公開

 非公開のときのがちょっとわかりにくい。ので自分用にまとめる。privateな要素を参照できるのは以下だけである。

  • private要素を定義したモジュールから参照可
  • 上記の子(子孫)モジュールから参照可

 逆にprivate要素を参照できないのは以下。

  • private要素を定義したモジュールの親(先祖)モジュールから参照不可

同一モジュール内なら参照可

 最初のコード例でいうとmy_modモジュール内でのみ参照可。たとえばmy_mod内のpublic()関数内ではprivate()関数を参照できる。

mod my_mod {
    pub fn public() -> String { private(); String::from("my_mod::public()") }
    fn private() -> String { String::from("my_mod::private()") }
}

子孫モジュールなら参照可

 my_modの子モジュールからもprivateを参照できる。super::private();がそれ。親モジュールのprivate要素を参照している。

mod my_mod {
    pub fn public() -> String { private(); String::from("my_mod::public()") }
    fn private() -> String { String::from("my_mod::private()") }
    mod my_mod_1 {
        pub fn public() -> String { super::private(); String::from("my_mod::public()") }
    }
}

 また、孫からも参照できる。super::super::private();がそれ。

mod my_mod {
    pub fn public() -> String { private(); String::from("my_mod::public()") }
    fn private() -> String { String::from("my_mod::private()") }
    mod my_mod_1 {
        pub fn public() -> String { super::private(); String::from("my_mod::my_mod_1::public()") }
        fn private() -> String { String::from("my_mod::my_mod_1::private()") }
        mod my_mod_2 {
            pub fn public() -> String { super::super::private(); super::private(); String::from("my_mod::my_mod_1::public()") }
        }
    }
}

 無駄な考察をしてみた。

 子モジュール側から参照できるのは、他言語でいうとC#のクラス継承におけるprotectedに似ている? でも、同名メンバだとthis.memberで自分か最も近い親の定義を参照するし、base.memberだと自分を除く最も近い親の定義を参照する。つまりそれより上にある同名メンバは参照できない。Rustではsuperで1階層ずつ指定せねばならないが、すべて個別に指定できる。継承の概念とは違うため。

 なぜprivateは自分のモジュール内だけでなく子孫モジュールから参照されてしまうのか。privateなら自モジュールのみ参照ではないのか。アクセスを細かく制御できるようにさせると複雑化するから? たとえばC#のアクセス修飾子は6種類もある。
 そもそも同一モジュール内しか使わないという場合が少ない? いやあるだろう。
 略記するため? 子孫モジュールからも参照できるようにすることでprotected的な修飾子を不要にする。そうすれば修飾子はpublicとprivate(protected)の2種類しかなくなる。2種類だけならpubを省略したときは必ずprivateとして断定できる。これにより略記可能。短いコードで書くことができる。

親モジュールからは参照不可

 最初のコードでmain()関数内でmy_mod::private()を呼び出せなかった。理由はmain()関数が定義されたモジュールmainmy_modの親モジュールだから。

 (Rust言語においてmain.rsファイル内はルートとなるモジュールでありmain()関数はエントリポイントとなる特殊関数)

main.rs

fn main() {
    println!("{}", my_mod::public());
//    println!("{}", my_mod::private()); // error[E0603]: function `private` is private
}
mod my_mod {
    pub fn public() -> String { String::from("my_mod::public()") }
    fn private() -> String { String::from("my_mod::private()") }
}

 「private要素は、先祖からは見えないのに子孫からは見れる」というのが直感的でない気がした。たぶんRust言語でprivateと言われているものは、今まで慣れ親しんできたオブジェクト指向におけるprotected相当だからだろう。

 覚えやすい考え方はないか。

 ソフトウェアは上位モジュールほど抽象化されてあるもの。そのため細かい処理は隠蔽されたほうが俯瞰しやすい。親側ほど参照できる要素を減らしたい。そう考えれば覚えやすいか?

まとめ

  • pubを要素の前に付与すると、そのモジュールを介してアクセス可
  • pubがないと、それを定義したモジュールとその子孫モジュールのみアクセス可

不明瞭

 なお、pubの対象となる要素は以下だと思う。

  • mod, fn, struct, enum, trait

 たぶんimplの前にpubは付与しないと思うのだが、どうなの?

 traitはまだ学習してないから知らん。でもググると以下でpub trait ...というコードを見た。

前回まで