Rustのエラー E0609: no field on type(存在しないフィールド)
E0609 は、value.something と書いたのに、value の型には something というフィールドがない、という意味のエラーです。たいていはタイプミスか、値が思っている型とちょっと違う(たとえば User ではなく Option<User> になっている)かのどちらかです。エラーメッセージには Rust から見えている正確な型が書かれていて、直すにはたいていそれだけで十分です。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0609]: no field `emial` on type `User` --> src/main.rs:12:25 |12 | println!("{}", user.emial); // ❌ タイプミス:フィールド名は `email` | ^^^^^ unknown field |help: a field with a similar name exists |12 - println!("{}", user.emial); // ❌ タイプミス:フィールド名は `email`12 + println!("{}", user.email); // ❌ タイプミス:フィールド名は `email` |「no field emial on type User」は「User 型に emial というフィールドはない」という意味です。help は「似た名前のフィールドがある」と教えてくれて、正しい書き方まで見せてくれています。
このエラーが出るのは、次のプログラムです。
struct User { name: String, email: String,}
fn main() { let user = User { name: String::from("Ferris"), }; println!("{}", user.name); println!("{}", user.emial); // ❌ タイプミス:フィールド名は `email`}エラーの意味
Section titled “エラーの意味”エラーの見出しは2つの部分に分かれていて、どちらも大事です。
no field `emial`:ドットのあとに書いた名前で、Rust が見つけられなかったものです。on type `User`:Rust がuserの型だと考えているものです。
ですから、まず名前を確認し、次に型を確認しましょう。メッセージの型が思っていた構造体でなければ(たとえば Option<User>、&Option<User>、Vec<User>、&T)、名前は合っていて、問題は先に構造体を取り出す必要があることです。
お店で「2段目の棚にある青いの」と頼んだのに、そもそも違うお店にいた、というようなものです。頼み方は合っていて、いる場所が違うだけなのです。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust では、構造体のフィールドは、構造体を書いた時点で決まります。コンパイラはフィールドの一覧を全部知っていて、.field をすべてコンパイル時にチェックします。JavaScript のように「実行時に探して undefined になる」ことも、Python のように AttributeError になることもありません。間違ったフィールド名が、プログラムに紛れ込むことはないのです。詳しくは構造体を見てください。
Option<T> や Result<T, E> のような包み込む型は、別の型です。あなたの構造体はその中に入っていますが、包み自体はその構造体のフィールドを持っていません。Rust が中まで手を伸ばしてくれないのは、値がそもそもないかもしれないからです。値がないときにどうするかは、自分で書く必要があります。詳しくはOptionを見てください。
ジェネリクスのコードも、同じ厳しいルールに従います。fn greet<T>(x: &T) の T はどんな型にもなれます。フィールドを持たない i32 や bool もです。Rust はジェネリックな関数を、ありうるすべての T に対して一度にチェックするので、トレイト境界が約束していることしか使わせてくれません。そして、トレイトが約束できるのはメソッドだけで、フィールドは約束できません。詳しくはジェネリクスとトレイトを見てください。
直し方1:つづりを直す
Section titled “直し方1:つづりを直す”メッセージの型が思っていたとおりの構造体なら、これを選びます。help: a field with a similar name exists(似た名前のフィールドがある)の行を探しましょう。たいていの場合、rustc はどのフィールドのつもりだったのかをちゃんとわかっています。
struct User { name: String, email: String,}
fn main() { let user = User { name: String::from("Ferris"), }; println!("{}", user.name); println!("{}", user.email); // ✅ 本当のフィールド名}実行結果:
Ferrisヒントが出ないときは、struct の定義に飛んで(たいていのエディタでは、型の上で Ctrl+クリックか F12)、そこから名前をコピーしましょう。フィールド名は大文字と小文字を区別します。userName と user_name は別のフィールドです。
直し方2:先に Option から構造体を取り出す
Section titled “直し方2:先に Option から構造体を取り出す”メッセージの型が Option<User>、Option<&User>、&Option<User> のときは、これを選びます。何かが見つからないかもしれない関数や、.first()、.last()、.get()、.find() のようなメソッドを使うと、しょっちゅう起こります。
error[E0609]: no field `name` on type `Option<User>` --> src/main.rs:11:25 |11 | println!("{}", user.name); | ^^^^ unknown field |help: one of the expressions' fields has a field of the same name |11 | println!("{}", user.unwrap().name); | +++++++++help は「式の中のフィールドの1つに、同じ名前のフィールドがある」と言って、.unwrap() を提案しています。このヒントどおりにすればコンパイルは通りますが、値が None だとプログラムがクラッシュします。それよりも、if let、match、あるいは map とデフォルト値を使って、両方の場合にどうするかを決めましょう。
struct User { name: String,}
fn find_user(id: u32) -> Option<User> { if id == 1 { Some(User { name: String::from("Ferris") }) } else { None }}
fn main() { // if let:User があるときだけコードを実行する if let Some(user) = find_user(1) { println!("見つかりました: {}", user.name); }
// match:両方の場合を扱う match find_user(2) { Some(user) => println!("見つかりました: {}", user.name), None => println!("IDが2のユーザーはいません"), }
// map + unwrap_or:フィールドを取り出すか、デフォルト値を使う let users = vec![User { name: String::from("Ada") }]; let first_name = users.first().map(|u| u.name.as_str()).unwrap_or("だれもいません"); println!("最初のユーザー: {first_name}");}実行結果:
見つかりました: FerrisIDが2のユーザーはいません最初のユーザー: AdaOption への参照にも、同じ書き方が使えます。maybe_user が &Option<User> なら、if let Some(user) = maybe_user と書けば user は &User になります(&Option<User> のときは rustc がヒントをまったく出さないので、覚えておく価値があります)。Option を返す関数の中なら、let user = find_user(1)?; でも大丈夫です。詳しくはmatchでOptionを扱うとif letを見てください。
直し方3:タプル構造体には .0 と .1 を使う
Section titled “直し方3:タプル構造体には .0 と .1 を使う”型が struct Point(i32, i32); のようなタプル構造体なら、これを選びます。タプル構造体のフィールドには名前がなく、位置しかありません。そのため p.x と書くと no field `x` on type `Point` になります(ヒントは p.0)。位置で取り出すか、名前つきの変数に分解しましょう。名前で呼びたいなら、名前つきフィールドの構造体にします。
struct Point(i32, i32); // タプル構造体:フィールドは .0 と .1
struct Size { width: u32, // 名前つきフィールド:.width と .height で使う height: u32,}
fn main() { let p = Point(3, 4); println!("x = {}, y = {}", p.0, p.1);
let Point(x, y) = p; // または名前つきの変数に分解する println!("距離の2乗 = {}", x * x + y * y);
let screen = Size { width: 1920, height: 1080 }; println!("{}x{}", screen.width, screen.height);}実行結果:
x = 3, y = 4距離の2乗 = 251920x1080ふつうのタプルも同じです。要素が2つのタプルで pair.2 と書くと E0609 になり、available fields are: `0`, `1`(使えるフィールドは 0 と 1)という note が付きます。詳しくは位置で要素を取り出すを見てください。
直し方4:ジェネリックな T には、フィールドの代わりにトレイトのメソッドを使う
Section titled “直し方4:ジェネリックな T には、フィールドの代わりにトレイトのメソッドを使う”メッセージが on type `&T`(または T)で、type parameter 'T' declared here(型パラメータ T はここで宣言された)というラベルが付いているときは、これを選びます。次のようなコードは、渡すつもりの型が全部 name フィールドを持っていても、エラーになります。
fn greet<T>(animal: &T) { println!("Hello, {}!", animal.name); // ❌ no field `name` on type `&T`}トレイトはフィールドを要求できませんが、ゲッターメソッドなら要求できます。小さなトレイトを定義して、それぞれの型に実装し、T の境界に付け足しましょう。
trait Named { fn name(&self) -> &str;}
struct Dog { name: String,}
struct Robot { model: String,}
impl Named for Dog { fn name(&self) -> &str { &self.name }}
impl Named for Robot { fn name(&self) -> &str { &self.model // フィールドの名前が同じである必要すらない }}
fn greet<T: Named>(thing: &T) { println!("こんにちは、{}!", thing.name()); // フィールドではなくメソッドの呼び出し}
fn main() { greet(&Dog { name: String::from("Rex") }); greet(&Robot { model: String::from("R2-D2") });}実行結果:
こんにちは、Rex!こんにちは、R2-D2!関数がいつも1つの型しか扱わないなら、ジェネリクスはそもそもいりません。fn greet(animal: &Dog) と書けば、animal.name が使えます。詳しくは関数の引数にトレイトを使うを見てください。
よく出会う場面
Section titled “よく出会う場面”() を付け忘れたメソッド。 word.len や cart.total(total がメソッドの場合)と書くと、E0609 に近いのですが、rustc は別のコードを出します。E0615、「attempted to take value of method」(メソッドを値として取り出そうとした)で、「use parentheses to call the method」(メソッドを呼ぶにはかっこを使う)というヒントが付きます。Vec で scores.len と書くと、E0616、「field len of struct Vec is private」(Vec 構造体の len フィールドは非公開)になります。Vec には本当にその名前の隠れたフィールドがあるからです。どちらも直し方は同じで、word.len()、scores.len()、cart.total() と書きます。それから、"hello".length は &str に対するふつうの E0609 です。Rust では len() という名前です。詳しくは文字列を見てください。
1つの要素ではなく Vec だったとき。 Vec<Dog> に対して dogs.name と書くと、no field `name` on type `Vec<Dog>` になります。name を持っているのはリストではなく、一つひとつの要素です。直し方:dogs[0].name のように1つ選ぶか、for dog in &dogs { println!("{}", dog.name); } のようにループします。詳しくはベクタを見てください。
値ではなく Result だったとき。 load_config().port と書くと、no field `port` on type `Result<Config, String>` になり、同じように .unwrap() のヒントが付きます。直し方:Result を返す関数の中で let config = load_config()?; と書くか、match を使います。詳しくは?演算子を見てください。
数値のフィールド。 u32 に対して age.value と書くと、また別のコードになります。E0610、「u32 is a primitive type and therefore doesn’t have fields」(u32 はプリミティブ型なので、フィールドを持たない)です。数値、bool、char には、フィールドがまったくありません。おそらく構造体の変数のつもりだったのでしょう。
関連するエラー
Section titled “関連するエラー”- E0599: no method named x found:このエラーのメソッド版です。タイプミスや、取り出す必要のある
Optionなど、原因も同じことがよくあります。 - E0425: cannot find value in this scope:ドットの前の変数がそもそも存在しない、またはフィールドの前に
self.を付け忘れた。 - E0308: mismatched types:
Optionをある場所では取り出し、別の場所では取り出さなかったときに、次によく出るエラーです。 E0615: attempted to take value of method:x.len()ではなくx.lenと書いた。E0616: field is private:フィールドはあるけれどpubではない。Vecのlenなどです。その型のメソッドを使いましょう。E0560: struct has no field named x:同じタイプミスを、構造体を作るときにした(User { emial: ... })。E0610: primitive type doesn't have fields:数値、bool、charに.somethingと書いた。- コンパイルエラーの一覧
理解度チェック
Section titled “理解度チェック”理解度チェック
1. エラーが「Option<User> 型に name というフィールドはない」と言っています。これは何を教えてくれていますか?
2. 値 m から、struct Meters(f64); の1つ目のフィールドを読むにはどう書きますか?
3. fn show<T>(x: &T) で、どの T からも名前を読めるようにするにはどうしますか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0609 — Rust error codes index(英語):このエラーの公式の説明です。
- 構造体を定義し、インスタンス化する — The Rust Programming Language 日本語版:名前つきフィールド、ドットでのアクセス、構造体更新記法を説明している章です。同じ章の後半で、
.0のように位置でフィールドにアクセスするタプル構造体も紹介しています。 - Enumを定義する — The Rust Programming Language 日本語版:「Option enumとNull値に勝る利点」の節で、
Option<T>とTが別の型である理由と、値を取り出す必要がある理由を説明しています。 - トレイト:共通の振る舞いを定義する — The Rust Programming Language 日本語版:トレイトのメソッドとトレイト境界です。ジェネリックなコードにはこれが正しい道具です。
- 構造体 — Rust By Example 日本語版:名前つきフィールドの構造体とタプル構造体を、実行できる例で比べています。