Rustのエラー E0063: missing field in initializer(フィールドが足りない)
E0063 は、Name { ... } で構造体を作るときに、すべてのフィールドに値を入れていない、という意味のエラーです。Rust がフィールドを勝手に埋めてくれることはないので、たとえ 0 や false や None でも、どのフィールドにも値が必要です。メッセージには足りないフィールドの名前が並ぶので、何を足せばいいかがすぐにわかります。Rust でいちばん親切なエラーのひとつです。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0063]: missing field `email` in initializer of `User` --> src/main.rs:8:16 |8 | let user = User { | ^^^^ missing `email`「missing field email in initializer of User」は「User の初期化で、フィールド email が足りない」という意味です。missing `email` は「email がない」です。
このエラーが出るのは、次のプログラムです。
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, // ❌ no `email` }; println!("{}", user.name);}コメントは「email がない」という意味です(エラーになる例なので、コードはそのままにしています)。
エラーの意味
Section titled “エラーの意味”見出しには、足りないフィールド(email)と構造体(User)が出ています。^^^^ は、1つの行ではなく、構造体を作り始めた構造体の名前を指しています。問題は、そこにないものだからです。
足りないフィールドが複数あると、rustc はそれをアルファベット順に並べます。2つか3つなら、全部表示されます。
error[E0063]: missing fields `debug`, `retries` and `timeout_secs` in initializer of `Config`4つ以上だと、3つだけ表示して、残りは数で教えてくれます。
error[E0063]: missing fields `debug`, `port`, `retries` and 1 other field in initializer of `Config` --> src/main.rs:10:18 |10 | let config = Config { | ^^^^^^ missing `debug`, `port`, `retries` and 1 other field「1 other field」は「ほかにもう1つ」という意味です。全部を知るには、struct Config の定義を見ましょう。
すべての欄が必須の申込書のようなものです。あとで書くつもりでも、「メールアドレス」の欄が空のままでは提出できません。
Rust がこうする理由
Section titled “Rust がこうする理由”多くの言語では、書き忘れたフィールドは黙って null や 0 や空文字列になります。するとプログラムはメールアドレスのないユーザーのまま動き続け、ずっと離れたどこかで(しかも本番環境で)壊れることがよくあります。Rust には null がなく、裏でこっそり値をでっち上げることもしません。User はいつも本物の email を持っています。詳しくは構造体を定義して作るを見てください。
だからといって、毎回すべてのフィールドを書かなければならないわけではありません。下の直し方で見るように、「デフォルト値を使って」や「残りはこっちの値からコピーして」を短く書く方法があります。大事なのは、自分で選ぶこと、そしてその選択がコードに書かれていることです。
直し方1:足りないフィールドを追加する
Section titled “直し方1:足りないフィールドを追加する”たいていはこれを選びます。フィールドがあるのには理由があるので、ちゃんとした値を入れましょう。フィールドと同じ名前の変数があるなら、User { name, email, active } という省略記法も使えます。
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, }; println!("{} <{}> 有効: {}", user.name, user.email, user.active);}実行結果:
Ferris <[email protected]> 有効: true直し方2:Default を導出して ..Default::default() を使う
Section titled “直し方2:Default を導出して ..Default::default() を使う”設定やオプションのように、フィールドが多くて、そのほとんどに「空」の値がはっきりしている構造体なら、これを選びます。構造体に #[derive(Default)] を付けます。そして、気になるフィールドだけを書いて、最後に ..Default::default() を付けます。.. は「書かなかったフィールドは全部この値から持ってきて」という意味です。
#[derive(Debug, Default)]struct Config { host: String, port: u16, debug: bool, timeout_secs: u64, retries: u8,}
fn main() { let config = Config { host: String::from("localhost"), port: 8080, ..Default::default() // ✅ debug、timeout_secs、retries はデフォルト値になる }; println!("{:?}", config);}実行結果:
Config { host: "localhost", port: 8080, debug: false, timeout_secs: 0, retries: 0 }導出したデフォルト値は、数値なら 0、bool なら false、String なら空の文字列、Option なら None です。0 がデフォルトでは困るなら(タイムアウト0秒はたぶん困ります)、もっと良い値で impl Default for Config を自分で書きましょう。詳しくはDefault:ちょうどいい初期値を見てください。
#[derive(Default)] を付け忘れると、エラーは E0277 に変わります。the trait bound `Config: Default` is not satisfied(Config が Default トレイトを実装していない)と出て、help: consider annotating `Config` with `#[derive(Default)]`(Config に #[derive(Default)] を付けることを検討してください)と教えてくれます。
直し方3:コンストラクタ new を書く
Section titled “直し方3:コンストラクタ new を書く”その構造体をいろいろな場所で作るときや、いつも同じ値で始まるフィールドがあるときは、これを選びます。構造体を作るコードを1つの関数にまとめて、ほかの場所ではその関数を呼びます。あとでフィールドを追加しても、直すのは new だけで、呼び出している場所を全部直す必要はありません。
struct Player { name: String, score: u32, level: u8,}
impl Player { fn new(name: &str) -> Player { Player { name: name.to_string(), score: 0, // 新しいプレイヤーはみんなスコア0で…… level: 1, // ……レベル1から始まる } }}
fn main() { let ana = Player::new("Ana"); let mut bo = Player::new("Bo"); bo.score += 50; println!("{}:スコア {}、レベル {}", ana.name, ana.score, ana.level); println!("{}:スコア {}、レベル {}", bo.name, bo.score, bo.level);}実行結果:
Ana:スコア 0、レベル 1Bo:スコア 50、レベル 1new の中で構造体を作るコードにも、もちろんすべてのフィールドが必要です。だから、書き忘れたフィールドはそこで E0063 として出てきます。それでいいのです。直すのは1か所だけで済みます。詳しくはコンストラクタのような関数を見てください。
直し方4:..other で残りを別の値からコピーする
Section titled “直し方4:..other で残りを別の値からコピーする”似た値がすでにあって、少しだけ変えたコピーが欲しいときは、これを選びます。..base は、書かなかったフィールドを全部 base から持ってきます。
#[derive(Debug, Clone)]struct Theme { name: String, font_size: u8, dark: bool,}
fn main() { let base = Theme { name: String::from("Default"), font_size: 14, dark: false };
// 変わるのは `name` と `dark` だけで、残りは `base` から let night = Theme { name: String::from("Night"), dark: true, ..base };
// `..base` だと name が base からムーブされるので、base を使い続けるために clone する let large = Theme { font_size: 20, ..base.clone() };
println!("{:?}", night); println!("{:?}", large); println!("{:?}", base);}実行結果:
Theme { name: "Night", font_size: 14, dark: true }Theme { name: "Default", font_size: 20, dark: false }Theme { name: "Default", font_size: 14, dark: false }1つ知っておいてほしいことがあります。..base は、String のようなフィールドを base からムーブします。night では問題ありません。name は自分で書いたので、base から来たのは Copy のフィールド(font_size)だけだからです。large では name が base から来るので、base を使い続けられるように ..base.clone() と書いています。clone せずにあとで base を使うと、E0382 になります。
Option のフィールドが役に立つとき
Section titled “Option のフィールドが役に立つとき”本当に空のことがあるフィールドもあります。ユーザーがまだ登録していない電話番号、ニックネーム、終了日などです。そういうフィールドは Option にしましょう。それでも値は書かなければなりません(Rust が None を勝手に入れてくれることはありません)が、None が正直で、ちゃんと許された値になります。詳しくはOptionを見てください。
#[derive(Debug, Default)]struct Contact { name: String, email: String, phone: Option<String>, // ないかもしれない nickname: Option<String>,}
fn main() { let a = Contact { name: String::from("Ana"), phone: None, // はっきり書く:「電話番号はまだない」 nickname: Some(String::from("Annie")), }; // Default を使うと、省いた Option はすべて None になる let b = Contact { name: String::from("Bo"), ..Default::default() };
println!("{} 電話: {:?}、ニックネーム: {:?}", a.name, a.phone, a.nickname); println!("{} メール: {:?}、電話: {:?}", b.name, b.email, b.phone);}実行結果:
Ana 電話: None、ニックネーム: Some("Annie")Bo メール: ""、電話: Noneb.email が "" になっていることに注目してください。これが Default の落とし穴です。必須のフィールドが、黙って空になってしまうのです。Option は本当に省略できるものだけに使い、email のような必須のフィールドは、いつも値を入れるふつうの型のままにしておきましょう。
よく出会う場面
Section titled “よく出会う場面”構造体にフィールドを追加した直後。 User に created_at を追加すると、プロジェクトの中のすべての User { ... } が突然 E0063 になります。これは Rust が手伝ってくれているのです。どの場所も、考えなければならない場所だからです。new のコンストラクタ(直し方3)があれば、このリストは短くて済みます。
名前付きのフィールドを持つ列挙型のバリアント。 Shape::Rectangle { width: 2.0 } は missing field `height` in initializer of `Shape`(Shape の初期化で、フィールド height が足りない)になります。メッセージに出るのはバリアントではなく列挙型の名前なので、気をつけましょう。直し方:そのバリアントのすべてのフィールドに値を入れます。詳しくは列挙型はデータを持てるを見てください。
構造体を返す関数。 fn new_player(name: &str) -> Player { Player { name: name.to_string(), score: 0 } } では、矢印が関数の中の Player を指して、missing `level` と表示されます。直し方:そこに level を追加します。
関連するエラー
Section titled “関連するエラー”- E0560: struct has no field named x:逆のパターン。構造体にないフィールドを書いた。タイプミスをすると、両方が同時に出ることがよくあります。
- E0559: variant has no field named x:列挙型のバリアントで、間違った名前を書いた。
- E0277: trait bound not satisfied:
..Default::default()と書いたのに、構造体がDefaultを導出していない。 - E0451: field is private:構造体が別のモジュールのもので、非公開のフィールドがあるので値を入れられない。非公開のフィールドを省いた場合は E0063 ではなく、「cannot construct
Accountwith struct literal syntax due to private fields」(非公開のフィールドがあるので、Accountを構造体リテラルで作れない)になります。どちらの場合も、モジュールのnew関数を使いましょう。 - E0382: use of moved value:
..valueでフィールドがムーブされたあとに、その値を使った。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. rustc が missing fields a, b, c and 2 other fields と言っています。足りないフィールドはいくつですか?
2. 構造体に、今はまだ必要のない Option<String> のフィールドがあります。何と書けばいいですか?
3. Config { port: 80, ..Default::default() } をコンパイルするには何が必要ですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0063 — Rust error codes index(英語):このエラーの公式の説明です。
- 構造体を定義し、インスタンス化する — The Rust Programming Language 日本語版:構造体の作り方、フィールド初期化省略記法、構造体更新記法を説明している章です。
Default— 標準ライブラリのドキュメント(英語):#[derive(Default)]、自分でのimpl Default、..Default::default()の使い方がわかります。- Struct expressions — The Rust Reference(英語):
Name { field: value }と..baseの正確なルールです。 - Constructors — Rust API Guidelines(英語):Rust のコードが構造体を作るのに
new関数を使う理由です。