Rustのエラー E0451: field of struct is private(構造体リテラルの非公開フィールド)
E0451 は、Account { ... } のような構造体リテラルで構造体を作ろうとしたけれど、フィールドの中に非公開のものがあった という意味のエラーです。構造体リテラルを書くには、すべてのフィールドを埋めなければなりません。そのため、非公開のフィールドが1つあるだけで締め出されてしまいます。これはわざとそうなっています。モジュールは、自分のコンストラクタを通して作ってほしいのです。直し方は、ほとんどの場合 new 関数を呼ぶ(なければ追加する)ことです。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0451]: field `balance` of struct `Account` is private --> src/main.rs:21:9 |19 | let account = bank::Account { | ------------- in this type20 | owner: String::from("Ferris"),21 | balance: 1_000_000, // ❌ private field | ^^^^^^^ private field「field balance of struct Account is private」は「構造体 Account のフィールド balance は非公開」という意味です。in this type は「この型の」、private field は「非公開のフィールド」というラベルです。
このエラーが出るのは、次のプログラムです。
mod bank { pub struct Account { pub owner: String, balance: i64, // private }
impl Account { pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
pub fn balance(&self) -> i64 { self.balance } }}
fn main() { let account = bank::Account { owner: String::from("Ferris"), balance: 1_000_000, // ❌ private field }; println!("{} has {}", account.owner, account.balance());}代わりに、よく似た別のエラーが出ることもあります。
| 書いたコード | rustc の表示 |
|---|---|
| リテラルに非公開のフィールドを書いた | error[E0451]: field `balance` of struct `Account` is private |
Config { verbose: true, ..Default::default() } |
これも E0451。..Default::default() を指す(直し方3を参照) |
| 非公開のフィールドを書かずにリテラルを書いた | cannot construct `Account` with struct literal syntax due to private fields。よく help: you might have meant to use the `new` associated function も一緒に出る |
タプル構造体で Percent(150) と書いた |
E0603:tuple struct constructor `Percent` is private |
3行目は「非公開のフィールドがあるので、構造体リテラルの構文では Account を作れない」という意味で、help は「関連関数 new を使うつもりだったのでは」と提案しています。4行目は「タプル構造体のコンストラクタ Percent は非公開」という意味です。どれも直し方は同じです。
エラーの意味
Section titled “エラーの意味”矢印は、設定してはいけないフィールド balance を指しています。owner は pub なので、設定してかまいません。でも、構造体リテラルは「全部か、ゼロか」です。balance を書かずに済ませることはできませんし(Rust は足りないフィールドを埋めてくれません)、書くこともできません。つまり mod bank の外では、Account { ... } と書く方法がそもそもないのです。
実際の銀行で口座を開くところを想像してみてください。申込書に、最初の残高を自分で書き込むことはできません。名前を書けば銀行が口座を作り、最初の残高はゼロだと銀行が決めます。Account::new は、その申込書にあたります。
Rust がこうする理由
Section titled “Rust がこうする理由”構造体のフィールドは、関数やモジュールと同じく、何もしなければ非公開です(モジュールを見てください)。もし外のコードが好きな残高で Account を作れたら、モジュールのルールは意味がなくなります。残高100万円の口座も、マイナス50円の口座も作れてしまいます。リテラルを書けるのが mod bank だけなので、プログラムの中のすべての Account は、ルールを知っているコードによって作られます。
もう1つ利点があります。あとから created_at や history のような非公開のフィールドを追加しても、モジュールの外のコードは壊れません。外のコードはリテラルを書いていないからです。呼んでいるのは new なので、new を1か所直すだけで済みます。
直し方1:pub fn new コンストラクタを使う(なければ追加する)
Section titled “直し方1:pub fn new コンストラクタを使う(なければ追加する)”ほとんどの場合、これを選びます。まず new(または from_...、with_...)という関数を探しましょう。rustc が教えてくれることもよくあります。自分の構造体で、そういう関数がなければ書きます。
mod bank { pub struct Account { pub owner: String, balance: i64, // 非公開 }
impl Account { // 口座はすべて0から始まる。お金が入るのは deposit() を通したときだけ。 pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
pub fn deposit(&mut self, amount: i64) { if amount > 0 { self.balance += amount; } }
pub fn balance(&self) -> i64 { self.balance } }}
fn main() { let mut account = bank::Account::new("Ferris"); account.deposit(250); println!("{}の残高: {}", account.owner, account.balance());}実行結果:
Ferrisの残高: 250mod bank の中ではリテラルが使えるので、new の中では Account { ... } を自由に書けます。new そのものに pub を付けるのを忘れないでください。付けないと E0624(associated function `new` is private、関連関数 new は非公開)になります。受け付けてはいけない入力があるなら、new が Option<Account> や Result<Account, String> を返すようにしましょう。E0603 のページにその例があります。コンストラクタについて詳しくは構造体を見てください。
直し方2:ただのデータなら、フィールドを pub にする
Section titled “直し方2:ただのデータなら、フィールドを pub にする”値がどんな組み合わせでもかまわないときは、これを選びます。色はただの3つの数字で、守るべき「間違った色」はありません。それなら、リテラルで作るのがいちばんわかりやすい方法です。
mod theme { pub struct Color { pub r: u8, pub g: u8, pub b: u8, }}
fn main() { let ferris_orange = theme::Color { r: 247, g: 76, b: 0 }; println!("#{:02x}{:02x}{:02x}", ferris_orange.r, ferris_orange.g, ferris_orange.b);}実行結果:
#f74c00pub はすべてのフィールドに必要です。自分のクレートの中だけで作れればいいなら、フィールドに pub(crate) を付けてもかまいません。
フィールドを非公開のままにすべきとき: 値が間違っている可能性があるとき(マイナスの残高、100を超えるパーセント、@ のないメールアドレス)や、2つのフィールドが互いに合っていなければならないとき(items の合計と一致すべき total)です。この場合、リテラルを許すとチェックを飛ばされてしまうので、直し方1が正解です。
直し方3:Default から始めて、公開のフィールドを変える
Section titled “直し方3:Default から始めて、公開のフィールドを変える”ちょうどいいデフォルト値のある「設定」の構造体で、公開のフィールドと非公開のフィールドが混ざっているときは、これを選びます。先に落とし穴を見ておきましょう。構造体更新記法の ..Default::default() は動きそうに見えますが、動きません。
mod settings { pub struct Config { pub verbose: bool, retries: u32, // private: must stay between 1 and 10 }
impl Default for Config { fn default() -> Config { Config { verbose: false, retries: 3 } } }}
fn main() { let config = settings::Config { verbose: true, ..Default::default() }; println!("{}", config.verbose);}error[E0451]: field `retries` of struct `Config` is private --> src/main.rs:15:54 |15 | let config = settings::Config { verbose: true, ..Default::default() }; | ^^^^^^^^^^^^^^^^^^ field `retries` is private「構造体 Config のフィールド retries は非公開」という意味です(コード中のコメントは「非公開:1から10の間でなければならない」です)。..Default::default() も、やはり構造体リテラルなのです。retries を新しい構造体にコピーするので、それを設定したことになります。代わりに、まずデフォルト値を作って、あとから公開のフィールドを変更しましょう。
mod settings { pub struct Config { pub verbose: bool, retries: u32, // 非公開:1から10の間でなければならない }
impl Default for Config { fn default() -> Config { Config { verbose: false, retries: 3 } } }
impl Config { pub fn retries(&self) -> u32 { self.retries } }}
fn main() { let mut config = settings::Config::default(); config.verbose = true; // 公開のフィールドは、あとから変更できる println!("verbose: {}, retries: {}", config.verbose, config.retries());}実行結果:
verbose: true, retries: 3すべてのフィールドが pub なら、..Default::default() は問題なく使えます。じゃまをするのは、非公開のフィールドだけです。
直し方4:ビルダー風のメソッドを用意する
Section titled “直し方4:ビルダー風のメソッドを用意する”オプションがいくつもあって、その中にルールのあるものが混ざっているときは、これを選びます。それぞれのメソッドは構造体を受け取り、設定を1つだけ(チェックしながら)変えて、構造体を返します。そのため、呼び出しをきれいにつなげて書けます。
mod settings { pub struct Config { verbose: bool, retries: u32, }
impl Config { pub fn new() -> Config { Config { verbose: false, retries: 3 } }
pub fn verbose(mut self, on: bool) -> Config { self.verbose = on; self }
// ルールはここにある:再試行は1回から10回まで。 pub fn retries(mut self, n: u32) -> Config { self.retries = n.clamp(1, 10); self }
pub fn describe(&self) -> String { format!("verbose: {}, retries: {}", self.verbose, self.retries) } }}
fn main() { let config = settings::Config::new().verbose(true).retries(50); println!("{}", config.describe());}実行結果:
verbose: true, retries: 1050回の再試行を頼んでも、何も言わずに10回になりました。ルールが1か所にまとまっているからです。多くのクレートがこの方法を使っていて、別の ConfigBuilder 型を用意し、最後に .build() を呼ぶこともあります。その形についてはデザインパターンを見てください。
よく出会う場面
Section titled “よく出会う場面”構造体をモジュールに移したあと。 main.rs の User { name, email } はどれもちゃんと動いていたのに、User を mod user(または src/user.rs)に移したとたん、全部エラーになります。構造体ごとに決めましょう。ただのデータなら pub のフィールドに(直し方2)、ルールのあるものには pub fn new を(直し方1)。
標準ライブラリやクレートの型。 Vec { ... } や Duration { ... } とは書けません。rustc は cannot construct `Vec<_, _>` with struct literal syntax due to private fields(非公開のフィールドがあるので、構造体リテラルの構文では Vec<_, _> を作れない)と表示し、Vec::new() や Vec::with_capacity(_) のような関数を並べてくれます。それらや、Duration::from_secs(5) を使いましょう。
ほかのクレートの #[non_exhaustive] な構造体。 この場合は、フィールドが公開されていても役に立たず、エラーコードも別のものになります。error[E0639]: cannot create non-exhaustive struct using struct expression(non-exhaustive な構造体は構造体式では作れない)。そのクレートは、あなたのコードを壊さずにあとでフィールドを追加したいのです。直し方1や直し方3とまったく同じように、コンストラクタか Default を使いましょう。
関連するエラー
Section titled “関連するエラー”- E0616:同じ非公開のフィールドですが、それを読むとき(
account.balance)に出ます。 - E0603:
tuple struct constructor `X` is private(タプル構造体のコンストラクタXは非公開)。非公開の関数、構造体、モジュールも。 - E0624:
associated function `new` is private(関連関数newは非公開)。コンストラクタにもpubが必要です。 - E0560:
struct `X` has no field named `y`(構造体Xにyというフィールドはない)。リテラルのフィールド名のタイプミスです。 - E0063(英語):
missing field `x` in initializer of `Y`(Yの初期化にフィールドxがない)。公開のフィールドを書き忘れた。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. mod tickets の中に pub struct Ticket { pub title: String, id: u32 } があります。main から Ticket を作るにはどうしますか?
2. Config に非公開のフィールドがあるとき、Config { verbose: true, ..Default::default() } がエラーになるのはなぜですか?
3. すべてのフィールドを pub にして、そのままリテラルで作るのに向いている構造体はどれですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0451 — Rust error codes index(英語):このエラーの公式の説明です。最小限の例が載っています。
- 構造体とenumを公開する — The Rust Programming Language 日本語版:非公開のフィールドを持ち、公開のコンストラクタが必要になる構造体の例です。
- 構造体の場合 — Rust By Example 日本語版:公開と非公開のフィールドを持つ構造体を、モジュールの外から作る例です。
- Builder pattern — Rust Design Patterns(英語):別のビルダー型を使った、完全なビルダーパターンです。
Defaultトレイト — 標準ライブラリのドキュメント(英語):デフォルト値を derive する方法と、自分で実装する方法がわかります。