コンテンツにスキップ

Rustのエラー E0063: missing field in initializer(フィールドが足りない)

E0063 は、Name { ... } で構造体を作るときに、すべてのフィールドに値を入れていない、という意味のエラーです。Rust がフィールドを勝手に埋めてくれることはないので、たとえ 0 や false や None でも、どのフィールドにも値が必要です。メッセージには足りないフィールドの名前が並ぶので、何を足せばいいかがすぐにわかります。Rust でいちばん親切なエラーのひとつです。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

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 がない」という意味です(エラーになる例なので、コードはそのままにしています)。

見出しには、足りないフィールド(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 の定義を見ましょう。

すべての欄が必須の申込書のようなものです。あとで書くつもりでも、「メールアドレス」の欄が空のままでは提出できません。

多くの言語では、書き忘れたフィールドは黙って 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"),
email: String::from("[email protected]"), // ✅ 追加した
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、レベル 1
Bo:スコア 50、レベル 1
▶ 実際に試してみよう

new の中で構造体を作るコードにも、もちろんすべてのフィールドが必要です。だから、書き忘れたフィールドはそこで 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"),
email: String::from("[email protected]"),
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 メール: ""、電話: None
▶ 実際に試してみよう

b.email が "" になっていることに注目してください。これが Default の落とし穴です。必須のフィールドが、黙って空になってしまうのです。Option は本当に省略できるものだけに使い、email のような必須のフィールドは、いつも値を入れるふつうの型のままにしておきましょう。

構造体にフィールドを追加した直後。 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 を追加します。

  • 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 Account with struct literal syntax due to private fields」(非公開のフィールドがあるので、Account を構造体リテラルで作れない)になります。どちらの場合も、モジュールの new 関数を使いましょう。
  • E0382: use of moved value:..value でフィールドがムーブされたあとに、その値を使った。
  • コンパイルエラーの一覧:エラー解説ページの全リストです。

理解度チェック

1. rustc が missing fields a, b, c and 2 other fields と言っています。足りないフィールドはいくつですか?

2. 構造体に、今はまだ必要のない Option<String> のフィールドがあります。何と書けばいいですか?

3. Config { port: 80, ..Default::default() } をコンパイルするには何が必要ですか?

スコア: 0 / 3