コンテンツにスキップ

Rustのエラー E0610: primitive type doesn't have fields(数値にフィールドはない)

E0610 は、something.field と書いたけれど、something はただの数値(または bool や char)で、そういう型にはフィールドがひとつもない、という意味のエラーです。ほとんどの場合、変数が思っている型になっていないことを表しています。構造体から数値をもう取り出していた、関数が構造体ではなく数値を返していた、.0 を1つ多く書いていた、などです。メッセージには Rust から見えている正確な型が書かれているので、直し方まであと一歩です。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
--> src/main.rs:3:22
|
3 | println!("{}", x.value); // ❌ x は数値なので、`value` フィールドはない
| ^^^^^

「{integer} is a primitive type and therefore doesn’t have fields」は「{integer} はプリミティブ型(組み込みの基本型)なので、フィールドを持たない」という意味です。

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

fn main() {
let x = 5;
println!("{}", x.value); // ❌ x は数値なので、`value` フィールドはない
}

バッククォートの中の型は、Rust がその変数の型だと考えているものです。ここでは `{integer}` で、「何かの整数。どの種類かはまだ決めていない」という意味です(ほかに何も決め手がなければ、Rust はあとで i32 を選びます)。小数なら `{float}`、ほかにも u32、usize、f64、bool、char のような具体的な型が表示されることもあります。

^^^^^ は、ドットのあとの名前を指しています。Rust は「value のつづりが違う」と言っているのではありません。「数値には、どんな名前でもフィールドはない」と言っているのです。

ですから、考えるべきなのは「なぜこの変数は数値なのか」です。「5 の値札」を見せてと頼むようなものです。商品には値札がありますが、5 はただの数です。

i32、u32、f64、bool、char のようなプリミティブ型は、言語に組み込まれた単一の値です。構造体ではないので、ドットと名前で取り出せる中身はありません。詳しくはデータ型を見てください。

ただし、メソッドは持っています。x.abs()、x.pow(2)、price.round()、c.is_alphabetic() などです。メソッドは () を付けて呼びます。フィールドは構造体の中にしまってあるデータで、フィールドを持てるのは構造体、タプル、共用体(union)だけです。

Rust はすべての .field をコンパイル時にチェックするので、この取り違えが実行時のクラッシュや、知らないうちに undefined になる、といった問題になることはありません。すぐに、ぴったりその行で気づけます。

構造体からフィールドをもう取り出しているときは、これを選びます。let count = counter.value; のあと、count は数値そのものなので、count.value は u32 のフィールドを求めていることになります。

error[E0610]: `u32` is a primitive type and therefore doesn't have fields
--> src/main.rs:8:26
|
8 | println!("{}", count.value);
| ^^^^^

数値をそのまま使うか、構造体に戻ってそこからフィールドを読みましょう。

struct Counter {
value: u32,
}
fn main() {
let mut counter = Counter { value: 0 };
counter.value += 3;
let count = counter.value; // count はもうただの u32
println!("{}", count); // ✅ 数値そのものを使う
println!("{}", counter.value); // ✅ または構造体からフィールドを読む
}

実行結果:

3
3
▶ 実際に試してみよう

変数の型がわからないときは、エディタで変数にマウスを乗せてみましょう(rust-analyzer を使います。Rustの開発ツールを見てください)。または、一時的に let _: () = count; と書いてみます。rustc が expected `()`, found `usize`(() を期待したが usize だった)と答えて、本当の型を教えてくれます。確認したら、その行は消してください。

直し方2:数値ではなく構造体を返す・しまう

Section titled “直し方2:数値ではなく構造体を返す・しまう”

値が関数やコレクションから来ていて、構造体だと思っていたときは、これを選びます。よくあるのは次の2つです。

  • fn find_order() -> u32 のような関数が注文の ID を返していて、order が u32 なので order.total が失敗する。
  • HashMap<&str, u32> にスコアだけをしまっていて、値がどれも Player ではなく u32 なので、scores["ana"].score が失敗する。

本当にほしいものは何かを決めましょう。複数のデータが必要なら、戻り値の型や値の型を構造体に変えます。

use std::collections::HashMap;
struct Order {
id: u32,
total: u32, // セント単位
}
// 数値だけでなく、Order をまるごと返す
fn new_order(id: u32, total: u32) -> Order {
Order { id, total }
}
struct Player {
score: u32,
level: u8,
}
fn main() {
let order = new_order(7, 1999);
println!("注文{}は{}セントです", order.id, order.total);
// マップにはスコアだけでなく、Player をまるごとしまう
let mut players: HashMap<&str, Player> = HashMap::new();
players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"];
println!("ana: スコア{}、レベル{}", ana.score, ana.level);
}

実行結果:

注文7は1999セントです
ana: スコア10、レベル2
▶ 実際に試してみよう

必要なのが数値だけなら、u32 のままにして、.field の部分を消すだけで大丈夫です。詳しくは構造体とHashMapを見てください。

エラーが .0 や .1 を指しているときは、これを選びます。タプルとタプル構造体は、フィールドを位置で表します。そのため、つい1段深く入りすぎてしまいがちです。let x = point.0; のあと、x はもう数値なので、x.0 は `{integer}` is a primitive type になります。struct Meters(u32); のようなラッパーでも同じです。1つ目の .0 で u32 を取り出せますが、2つ目の .0 は失敗します。

struct Meters(u32);
fn main() {
let point = (3, 4);
let x = point.0; // x はもう数値の 3
println!("x = {}", x); // x.0 ではない
let m = Meters(42);
println!("{} m", m.0); // .0 を1つ付けると、タプル構造体から u32 を取り出せる
let line = ((0, 0), (3, 4)); // 入れ子のタプル:1段ごとに位置を1つ
println!("終点の y = {}", line.1.1);
}

実行結果:

x = 3
42 m
終点の y = 4
▶ 実際に試してみよう

段の数を数えましょう。.0 や .1 を1つ書くたびに、タプルの中に1段入ります。数値にたどり着いたら、そこで止まります。位置がわかりにくくなってきたら、代わりに構造体で名前を付けましょう。詳しくは位置で要素を取り出すとタプルと構造体の使い分けを見てください。

() のないメソッド。 f64 に price.round、i32 に x.abs と書いたときは、E0610 ではなく E0615「attempted to take value of method」(メソッドを値として取り出そうとした)になります。これらの名前はメソッドだからです。直し方:price.round()、x.abs()。ひとつ意外な点があります。型がまだ {integer} のまま(let x = -5; など)だと、x.abs でも E0610 になり、x.abs() と書くと今度は E0689「can’t call method abs on ambiguous numeric type {integer}」(あいまいな数値型 {integer} ではメソッド abs を呼べない)になります。let x: i32 = -5; のように型を決めれば、x.abs() が使えます。

数値への参照。 変数が u32 ではなく &u32 だと、エラーコードは E0609 の no field `value` on type `&u32` に変わります。よくあるのは for (name, score) in &players のようなループで、score が &i32 なので、score.1 が no field `1` on type `&{integer}` になる場面です。原因も直し方も同じで、score はもう数値です。

len() や count() の結果。 let len = scores.len(); で得られるのは usize なので、len.count は `usize` is a primitive type で失敗します。直し方:len をそのまま使います。

単位付きの数値。 price.cents や distance.meters と書きたいなら、小さな構造体を作りましょう。struct Price { cents: u64 } です。こうすれば price.cents が使えて、値段とほかの数値を取り違えることも型が防いでくれます。詳しくは構造体を見てください。

理解度チェック

1. let total = cart.total;(total は u32 のフィールド)のあとで total.amount と書くと、どうなりますか?

2. メッセージの {integer} はどういう意味ですか?

3. let x = point.0; のあとで x.0 と書くと E0610 になります。どう直しますか?

スコア: 0 / 3