コンテンツにスキップ

Rustのエラー E0560: struct has no field named x(構造体にないフィールド名)

E0560 は、Name { field: value } で構造体を作るときに書いたフィールドのどれかが、その構造体に存在しない、という意味のエラーです。たいていはタイプミスか、名前が変わったフィールドか、別の構造体のフィールドです。rustc が正しい名前をそのまま教えてくれることも多いので、いちばん早く直せるエラーのひとつです。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0560]: struct `User` has no field named `nmae`
--> src/main.rs:9:9
|
9 | nmae: String::from("Ferris"), // ❌ タイプミス:フィールド名は `name`
| ^^^^ unknown field
|
help: a field with a similar name exists
|
9 - nmae: String::from("Ferris"), // ❌ タイプミス:フィールド名は `name`
9 + name: String::from("Ferris"), // ❌ タイプミス:フィールド名は `name`
|

「struct User has no field named nmae」は「構造体 User に nmae という名前のフィールドはない」という意味です。unknown field は「知らないフィールド」、help は「似た名前のフィールドがある」と教えてくれていて、正しい書き方も見せてくれています。

このエラーが出るのは、次のプログラムです。

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
nmae: String::from("Ferris"), // ❌ タイプミス:フィールド名は `name`
email: String::from("[email protected]"),
active: true,
};
println!("{}", user.email);
}

見出しには、構造体(User)と自分が書いたフィールド(nmae)が出ています。^^^^ は、User { ... } の中のそのフィールドを指しています。Rust はそれを struct User のフィールドの一覧と比べて、一致するものを見つけられなかったのです。

矢印の下には、次の3つのヒントのどれかが出ます。それぞれ伝えていることが違います。

  • help: a field with a similar name exists(似た名前のフィールドがある):タイプミスです。rustc が正しい名前の行を見せてくれます。
  • note: available fields are: `username`(使えるフィールドは username):近い名前がありません。まだ値を入れていないフィールドの一覧です。
  • note: all struct fields are already assigned(構造体のフィールドはすべて代入済み):本当のフィールドは全部埋めていて、このフィールドは余分です。

rustc が、name が足りないことを別に指摘していない点に注目してください。タイプミスを直せば、両方の問題がいっぺんに消えます。

紙の申込書に記入していて、「FAX番号」という欄に電話番号を書こうとするようなものです。申込書にそんな欄はないので、窓口で書き直してくださいと返されてしまいます。

構造体のフィールドは、struct User { ... } と書いた時点で決まります。User を作るたびに、Rust はちょうどそのフィールドが過不足なく書かれているかをチェックします。足りないものも、余分なものも、つづりの違うものもあってはいけません。詳しくは構造体を定義して作るを見てください。

これは本物のバグを防いでくれます。JavaScript では、{ nmae: "Ferris" } と書くと、間違ったキーのオブジェクトが黙って作られ、ずっとあとのどこかで user.name が undefined になります。Rust では、タイプミスはコンパイラを通り抜けられないので、プログラムの中のどの User も、本当に name を持っています。

a field with a similar name exists と出たら、これを選びます。help: の行にある名前を使いましょう。フィールド名は大文字と小文字を区別しますし、Rust では snake_case を使うので、userName と user_name は別の名前です。

フィールド初期化省略記法(field init shorthand)にも気をつけましょう。User { nmae, email } は User { nmae: nmae, email: email } という意味なので、変数の名前がフィールド名と一致していなければなりません。変数の名前を変えるか、フィールドを省略せずに User { name: nmae, email } と書きます。

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);
// 省略記法:変数名がフィールド名と一致している
let name = String::from("Ada");
let email = String::from("[email protected]");
let other = User { name, email, active: false };
println!("{}({})有効: {}", other.name, other.email, other.active);
}

実行結果:

Ferris([email protected])有効: true
Ada([email protected])有効: false
▶ 実際に試してみよう

直し方2:フィールドの新しい名前を使う、またはフィールドを追加する

Section titled “直し方2:フィールドの新しい名前を使う、またはフィールドを追加する”

「似た名前」のヒントが出ないときは、これを選びます。構造体のほうが変わったことがよくあります。誰かが name を username に変えた、あるいはフィールドを消したのに、このコードはまだ古い名前を使っている、という場合です。rustc は残っているフィールドを一覧にしてくれます。

error[E0560]: struct `User` has no field named `name`
--> src/main.rs:8:9
|
8 | name: String::from("Ferris"),
| ^^^^ `User` does not have this field
|
= note: available fields are: `username`

`User` does not have this field は「User にこのフィールドはない」という意味です。

まっとうな選択肢は2つです。構造体が正しいなら、今のフィールド名を使います。自分のコードが正しくて、構造体に本当にそのフィールドが必要なら、構造体の定義に追加します。次の例では、Config で debug に対して all struct fields are already assigned が出ていたので、構造体に debug を追加して直しています。

struct User {
username: String, // `name` から名前が変わった:新しい名前を使う
email: String,
}
struct Config {
port: u16,
host: String,
debug: bool, // コードで必要なので、構造体に追加した
}
fn main() {
let user = User {
username: String::from("ferris"),
email: String::from("[email protected]"),
};
println!("{} ({})", user.username, user.email);
let config = Config {
port: 8080,
host: String::from("localhost"),
debug: true,
};
println!("{}:{} debug={}", config.host, config.port, config.debug);
}

実行結果:

localhost:8080 debug=true
▶ 実際に試してみよう

構造体が、アップデートしたばかりのクレートのものなら、新しいバージョンでフィールドの名前が変わったのかもしれません。そのバージョンの構造体を docs.rs(英語)で確認するか、クレートの変更履歴(changelog)を見てください。自分の構造体にフィールドを追加したら、その構造体を作っているすべての場所でも、そのフィールドに値を入れる必要があります。rustc が E0063 で1か所ずつ教えてくれます(下を見てください)。

書いたフィールドは存在するけれど、別の構造体のものだったときは、これを選びます。title を持つ Product と name を持つ Customer のように、似た構造体が2つあると、取り違えやすくなります。

error[E0560]: struct `Product` has no field named `name`
--> src/main.rs:13:9
|
13 | name: String::from("Keyboard"),
| ^^^^ `Product` does not have this field
|
= note: available fields are: `title`

available fields の注記を見ましょう。思っていなかったフィールドが並んでいたら、作ろうとしている構造体が違います。正しい構造体の名前を使うか、本当に作りたかった構造体のフィールド名を使いましょう。

struct Product {
title: String,
price: u32, // セント単位
}
struct Customer {
name: String,
email: String,
}
fn main() {
let keyboard = Product {
title: String::from("Keyboard"), // 商品には `title` がある
price: 4900,
};
let buyer = Customer {
name: String::from("Ana"), // お客さんには `name` がある
email: String::from("[email protected]"),
};
println!("{}さんが{}を{}セントで購入しました", buyer.name, keyboard.title, keyboard.price);
println!("領収書の送り先: {}", buyer.email);
}

実行結果:

AnaさんがKeyboardを4900セントで購入しました
領収書の送り先: [email protected]
▶ 実際に試してみよう

直し方4:タプル構造体にはかっこを使う

Section titled “直し方4:タプル構造体にはかっこを使う”

構造体が struct Point(i32, i32); のようなタプル構造体のときは、これを選びます。タプル構造体のフィールドには名前がないので、Point { x: 1, y: 2 } と書くと、x と y の両方で E0560 になります。このとき help: `Point` is a tuple struct, use the appropriate syntax(Point はタプル構造体なので、それに合った書き方をしてください)と、Point(/* i32 */, /* i32 */) という提案が表示されます。Point(1, 2) のようにかっこで作り、.0 と .1 で読みましょう。名前を付けたいなら、定義を struct Point { x: i32, y: i32 } に変えます。詳しくはタプルと構造体の使い分けを見てください。

E0560 の双子は E0063 です。構造体にあるフィールドなのに、値を入れていない、というエラーです。Rust がフィールドを勝手に埋めてくれることはないので、どのフィールドにも値が必要です。

error[E0063]: missing field `active` in initializer of `User`
--> src/main.rs:8:16
|
8 | let user = User {
| ^^^^ missing `active`

「missing field active in initializer of User」は「User の初期化で、フィールド active が足りない」という意味です。

いくつも足りないときは、missing fields `active`, `email` and `login_count` in initializer of `User` のように全部並べてくれます。直し方:すべてのフィールドに値を入れます。多くのフィールドに妥当なデフォルト値があるなら、Default を導出して、構造体更新記法 .. で残りを埋めましょう。

#[derive(Debug, Default)]
struct Settings {
volume: u8,
dark_mode: bool,
language: String,
}
fn main() {
// すべてのフィールドを書く
let full = Settings { volume: 80, dark_mode: false, language: String::from("en") };
// 気になるフィールドだけ書いて、残りは Default から
let quick = Settings { dark_mode: true, ..Default::default() };
// 残りを別の値からコピーする
let louder = Settings { volume: 100, ..full };
println!("{:?}", quick);
println!("{:?}", louder);
}

実行結果:

Settings { volume: 0, dark_mode: true, language: "" }
Settings { volume: 100, dark_mode: false, language: "en" }
▶ 実際に試してみよう

デフォルト値は、数値なら 0、bool なら false、String なら空の文字列です。別のデフォルト値にしたいなら、impl Default for Settings を自分で書きましょう。詳しくはDefault:ちょうどいい初期値を見てください。

.. の構造体更新記法の中。 Settings { dark: true, ..defaults } も E0560 になります。この場合、注記にはすべてのフィールドが並び(available fields are: `volume`, `dark_mode`, `language`)、名前が離れすぎていると「似た名前」のヒントは出ないこともあります。直し方:dark_mode: true。

名前付きフィールドを持つ列挙型のバリアント。 enum Shape { Circle { radius: f64 } } に対して Shape::Circle { r: 2.0 } と書くのも同じ間違いですが、rustc は別のコード E0559 を使います。variant `Shape::Circle` has no field named `r`(バリアント Shape::Circle に r という名前のフィールドはない)と表示され、同じ available fields の注記やタイプミスのヒントが付きます。直し方:Shape::Circle { radius: 2.0 }。詳しくは列挙型はデータを持てるを見てください。

別のモジュールの構造体。 フィールドは存在するけれど pub ではない場合は、代わりに E0451「field balance of struct Account is private」(構造体 Account のフィールド balance は非公開)が出ます。直し方:Account::new(...) のような、モジュールが用意しているコンストラクタを使います。詳しくは何もしなければすべて非公開を見てください。

理解度チェック

1. rustc が note: all struct fields are already assigned と言っています。どういう意味ですか?

2. let nmae = ...; があって、User { nmae, email } と書きました。どう直しますか?

3. active を書かずに User { name, email } のようにフィールドを省いたときに出るエラーはどれですか?

スコア: 0 / 3