問題の発生
コードを整理するために別のモジュールに移動した直後、コンパイルができなくなったという状況ではないでしょうか。Rustはデータに対して「必要最小限の公開」というアプローチをとっています。デフォルトでは、構造体自体が public とマークされていても、すべての構造体フィールドはプライベート(非公開)です。
このエラーは通常、リファクタリング中に発生します。たとえば、300行の単一の main.rs からマルチファイル構成のプロジェクトに移行した際、サブモジュールが互いの内部データを見ることができなくなったことに気づくかもしれません。フィールドが明示的にエクスポート対象としてマークされていない場合、コンパイラはカプセル化を守るためにアクセスをブロックします。
エラーメッセージ
この問題が発生したとき、コンパイラは非常に具体的に指摘します。ターミナルには以下のような出力が表示されます:
error[E0616]: field `balance` of struct `Account` is private
--> src/main.rs:10:20
|
10 | println!("{}", account.balance);
| ^^^^^^^ プライベートなフィールド
なぜRustは厳格なのか
JavaやC#の経験がある場合、パブリッククラスはそのメンバーも自動的に公開されると期待するかもしれません。しかし、Rustではすべてのフィールドに対して明示的な許可が必要です。たとえ pub struct User と定義しても、その内部のフィールドは特定のモジュールの外にあるコードからは隠されたままです。この厳格さにより、アプリケーションのあらゆる部分が他のあらゆる部分の内部状態に干渉する「スパゲッティコード」を防いでいます。
ステップバイステップの修正方法
方法1:簡単な修正 (pub)
フィールドが本当にパブリックAPIの一部であるべきなら、pub キーワードを追加します。これは、Point や Config 構造体のように、保護すべき複雑な内部ルールがない単純なデータコンテナにおける標準的なアプローチです。
修正前:
pub struct User {
username: String,
}
修正後:
pub struct User {
pub username: String, // モジュールの外からアクセス可能になります
}
方法2:中間の解決策 (pub(crate))
プロジェクト全体からはフィールドを見えるようにしたいが、ライブラリの外部ユーザーからは隠しておきたい場合はどうすればよいでしょうか?その場合は pub(crate) を使用します。これにより、外部APIをクリーンで安全に保ちつつ、自身のプロジェクト内での可視性を許可できます。
pub struct Database {
pub(crate) connection_string: String, // プロジェクト内でのみ可視
password: String, // このモジュール内でのみ厳格にプライベート
}
また、pub(super) を使用して、可視性を親モジュールのみに制限することもできます。
方法3:プロフェッショナルな選択(ゲッターメソッド)
フィールドを直接公開することは、しばしばリスクを伴う習慣です。後で内部データ型を変更したくなったときに、それを使用しているすべてのコードを修正しなければならなくなるからです。代わりに、フィールドはプライベートのままにし、データを読み取るためのパブリックメソッドを提供します。
src/models.rs 内:
pub struct Account {
balance: f64,
}
impl Account {
pub fn new(amount: f64) -> Self {
Self { balance: amount }
}
// 読み取り専用のゲッター
pub fn balance(&self) -> f64 {
self.balance
}
}
src/main.rs 内:
mod models;
use models::Account;
fn main() {
let my_acc = Account::new(1500.50);
// println!("{}", my_acc.balance); // これによりE0616が発生します
println!("現在の残高: {}", my_acc.balance()); // これは動作します!
}
確認方法
ターミナルで cargo check を実行して修正を確認してください。コード生成フェーズをスキップするため、フルビルドよりも大幅に高速です。エラー E0616 が消えていれば、可視性の設定は正しく行われています。ゲッターメソッドを選択した場合は、呼び出し側のコードを account.balance ではなく account.balance() 構文を使用するように更新したか確認してください。
可視性に関するプロのヒント
- **80/20の法則:** 構造体フィールドの80%はプライベートに保つことを目指しましょう。プログラムの残りの部分が機能するために絶対に必要なものだけを公開します。
- **子モジュールのアクセス:** Rustでは、子モジュールは親のプライベートフィールドを見ることができますが、親モジュールは子のプライベートフィールドを見ることはできません。
- **バリデーションロジック:** `user_age` のような値が負にならないように保証する必要がある場合は、プライベートフィールドと「セッター」メソッドを組み合わせて使用します。
- **クレートレベルのプライバシー:** 大規模なプロジェクトでは `pub(crate)` を多用し、エンドユーザーに内部の実装詳細が漏れないようにします。

