RFC2126の一部だが、Rust2018までに安定化する必要はないと判断され今に至る。元々の提案・実装ではなかったのですが、タプル構造体のメンバーに対して使ったときの構文的な曖昧性を取り除くために追加されました。放り込めて便利だからだと言われています。よる可視性指定もタプル構造体のメンバーに使ったときの構文的な曖昧性が指摘されています。可視性チェックあるアクセスが可視性を満たしているかどうかは「どのDef(或いはresolution)を」「どのモジュールから見るか」によって決定されます。それぞれアクセス権があるかどうかチェックされます。自身の可視性に基づいてチェックされます。されているかによってはアクセスできない場合もあります。このこと自体は割と当たり前の挙動です。外部の名前をスコープに置くための便宜的な用途でも使いますが、それが外から見えてしまっては困るからです。無視して、元々あったモジュールの親子関係(=モジュール木)に基づいて判定します。そのため、この判定はモジュールの親リンクを辿るだけで可能です。挙動にも影響を与えます。可視性判定は同時進行します。複数の名前空間にまたがって処理されるので、個々の名前空間ではインポートの失敗が許容されています。場合、各名前空間ごとの処理では以下のような扱いになります。アクセスできない場合は、その名前空間ではインポートしない。場合は、弱いほうの可視性でインポートする。インポートできたものが1つ以上ないとエラーになります。globインポートの場合はいくつか例外があり、たとえば何もインポートしなかった場合はエラーにはなりません。構文があるため、可視性の中でもパス解決をする必要があります。この可視性のパス解決はresolve_visibilityという専用のメソッドで行われています。Rust2015モードではデフォルト絶対パスです。その他に以下のような特徴があります。絶対パス・相対パス以外は未対応。Rust2018モードで行うとエラーになる。上記のように名前解決と密接に関わっていますが、それ以外の可視性は以下のタイミングで検査されます。インポート解決に使わないパスは、パス解決の段階でエラーになります。型に関連した可視性は、型解決の段階で検査されます。関連型の場合、メソッドの場合、フィールドの場合)private-in-publicprivate-in-publicはRFC0136の通称です。可視性に関する規定もここに含まれていますが、これは今となっては扱いがかなり違うので本節の対象外とします)RFC0136は、公開APIの型も公開されている必要があるという規定です。ある必要があるなどの規定があります。ある必要はないなどがあります。このような可視性は名前解決よりも後のフェーズで検査されます。private-in-publicのlintへの緩和RFC2145によりprivate-in-public規則が整理されました。既存の規則がより明確に説明されました。到達可能性に基づく新しい規則が提案されました。それにあわせて、既存のprivate-in-publicチェックをlintに緩和することが提案されました。呼ばれる種類のパターンを許容しています。外部クレートはVoldemortにアクセスする手段がない。m1::Voldemort}RFC2145で提案されている新しい規則では、ヴォルデモート型に対する対応も含まれます。dependenciesprivate-in-publicに関連して、RFC1977ではこれを依存関係に対して拡張することを提案しています。以下のように書いたとします。メジャーバージョンを上げたときは(通常は)自身のメジャーバージョンも上げる必要があると解釈されます。compatibilityを機械的に調べる方法は今のところないので、これはインフォーマルにそう定められているだけといえる)またこの情報を、三角依存関係を同じバージョンに解決するかどうかの判定にも使えないかと考えられています。全てに依存していたとします。単一バージョンに解決する必要はないことになります。とインポート解決の挙動について説明しました。ここまでを理解すれば、モジュール構造の設計で(挙動の予測ができずに)困ることはほぼなくなるのではないでしょうか。それに関連して、名前解決以降のフェーズでの可視性検査も軽く説明しました。インポートの解決までがようやく説明できたので、これを前提に次回はパス解決を説明を説明します。Rust2018以降、Rustのモジュールシステムは入門しやすく、かつマスターしにくくなりました。そこで本記事では、その両方を達成することを目指しました。同じテーマで、Rust2018を考慮して改めてまとめ直したものです。本記事中のサンプルコードの多くは、Rustの細かい仕様や実装を調べるためのものであり、実際に有用なプログラミングパターンであるとは限りません。実質的にバグに近い挙動を説明しているものもあるため、将来にわたって同じ動作であることはあまり保証できません。解決する必要があります。これをインポート解決といいます。内のパスだけ解決すればよいことに注意しましょう。それ以外のさまざまなパスはインポートのあとに解決されます。構文は何段階かに分けて進化してきましたが、本質的には昔から変化しておらず、以下の2つしかありません。ops::*,};......以下のように分解することができます。std::ops::*;ただし、この展開では以下のようなケースに注意が必要です。std::intrinsics::{};このようなケースではダミーインポートが生成され、それによって当該モジュールにアクセス権があることを担保しています。以下のようなインポートは形式的に禁止されています。itemのインポートと自分自身からのglobインポートは意味的に弾かれます。デフォルトで絶対パスが用いられていました。先頭に付与されたかのように扱われます。グローバル2015モードである。パスの先頭セグメントがRust2015モードのクレート由来である。パスの先頭セグメントがキーワードではない。個別のuseに分解した状態で上の規則が適用されます。処理は簡単で、指定したクレートのルートを指定した場所に継ぎ木するだけです。処理は簡単ではありません。順序よく解決されるとは限らないからです。指令があったかだけ覚えておいて、最小不動点アルゴリズムにより可能な部分から解決していきます。A;}以下では、このことを念頭に置きつつ話を進めていきます。単独別名インポートの規則は比較的簡単ですが、名前空間についてだけ気をつけることがあります。型名前空間しかインポートしない。どの名前空間でも利用不可能だったらエラー。利用可能になるかどうかはこの時点では確定していないことになります。存在することを仮定して処理を進めることはできないというわけです。globインポートの解決globインポートはより複雑な仕様を持っています。(RFC1560にその経緯などが書いてあります。以下のように処理されます。直下にある利用可能な全ての名前空間・名前をインポートする。globインポート以外の定義がすでに存在したら、そちらが優先される。globインポート同士で衝突した場合は、個別に以下のルールで仲裁される。}}最小不動点アルゴリズムこれらの条件を踏まえつつ、実際には解決途中の状態も考慮に入れながら処理しています。以下のようなコードを考えます。解決する必要があります。これの候補は2つあります。crate::m3::*;この2つのうちどちらに解決されるかは、前者がどの名前空間でインポートされるかに依存します。どの名前空間に存在するかを知る必要があります。このように、インポートの解決順序はあまり自明ではありません。前述のように、名前解決の最初の処理はモジュール木の構築です。処理は比較的簡単です。呼ぶことでモジュールにDefを紐付けることができます。Moduleの主要なデータは以下の通りです。