Rust 错误 E0610:primitive type doesn't have fields(基本类型没有字段)
E0610 的意思是:你写了 something.field,可 something 只是一个普通的数字(或者 bool、char),而这些类型根本没有字段。 这几乎总是说明,这个变量并不是你以为的那种东西:也许你之前已经把数字从结构体里取出来了,也许某个函数返回的是数字而不是结构体,又或者你多写了一个 .0。错误信息会告诉你 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} 是基本类型,所以没有字段”。
产生这个错误的程序是这样的:
fn main() { let x = 5; println!("{}", x.value); // ❌ x 是数字,没有 `value` 字段}这个错误是什么意思
Section titled “这个错误是什么意思”反引号里的类型,就是 Rust 认为你的变量是什么。这里是 `{integer}`,意思是“某种整数,具体是哪一种我还没定”(如果没有别的线索,Rust 最后会选 i32)。你还可能看到表示小数的 `{float}`,或者 u32、usize、f64、bool、char 这样的具体类型。
^^^^^ 指的是点号后面的名字。Rust 并不是在说“value 拼错了”,而是在说“数字不可能有任何字段,不管你叫它什么”。
所以要问的问题是:这个变量为什么是一个数字? 这就像去找“数字 5 上的价格标签”。商品才有价格标签,5 只是一个数字而已。
Rust 为什么要这样做
Section titled “Rust 为什么要这样做”i32、u32、f64、bool、char 这些基本类型(primitive type)是语言内置的单个值。它们不是结构体,里面没有任何可以用“点号 + 名字”取出来的东西。参见数据类型。
不过它们有方法:x.abs()、x.pow(2)、price.round()、c.is_alphabetic()。调用方法要写 ()。字段则是存放在结构体里的数据,只有结构体、元组和联合体(union)才有字段。
因为 Rust 在编译时就会检查每一个 .field,这种混淆永远不会变成运行时的崩溃,也不会悄悄变成一个 undefined。你马上就能知道,而且精确到哪一行。
修复 1:直接使用这个数字
Section titled “修复 1:直接使用这个数字”当你之前已经把字段从结构体里取出来时,选这个方法。写完 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); // ✅ 或者从结构体里读取字段}输出:
33不确定一个变量是什么类型?在编辑器里把鼠标悬停在它上面就行(需要 rust-analyzer,参见 Rust 开发工具)。或者临时写一行 let _: () = count;:rustc 会回答 expected `()`, found `usize`,告诉你真正的类型。看完记得把这一行删掉。
修复 2:返回或存放结构体,而不是数字
Section titled “修复 2:返回或存放结构体,而不是数字”当这个值来自某个函数或集合,而你以为它是结构体时,选这个方法。两种常见情况:
- 像
fn find_order() -> u32这样的函数只返回订单的 ID,于是order.total报错,因为order是一个u32。 HashMap<&str, u32>只存了分数,于是scores["ana"].score报错,因为每个值都是u32,而不是Player。
先想清楚你真正需要什么。如果需要好几项数据,就把返回类型或值的类型改成结构体:
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);
// map 里存的是完整的 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 那部分去掉就好。参见结构体和哈希 map。
修复 3:去掉多余的 .0
Section titled “修复 3:去掉多余的 .0”当错误指向 .0 或 .1 时,选这个方法。元组和元组结构体用位置来表示字段,很容易多往里走一层。写完 let x = point.0; 之后,x 已经是数字了,所以 x.0 会报 `{integer}` is a primitive type。像 struct Meters(u32); 这样的包装类型也一样:一个 .0 取出里面的 u32,再来一个 .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.0); // 一个 .0 就能从元组结构体里取出 u32
let line = ((0, 0), (3, 4)); // 嵌套元组:每一层用一个位置 println!("终点 y = {}", line.1.1);}输出:
x = 342 米终点 y = 4数一数层数:每个 .0 或 .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}”(不能在类型不明确的数字上调用方法)。给它一个类型,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 就能用了,而且类型系统还会防止你把价格和别的数字搞混。参见结构体。
- E0609:no field
xon typeT:这个类型没有叫这个名字的字段。对&u32这样的引用,你会得到它(而不是 E0610)。 - E0615:attempted to take value of method:你写了
price.round,而不是price.round()。 E0689: can't call method on ambiguous numeric type:在类型还没确定的数字上调用方法。加上i32这样的类型就好。- E0599:no method named x found:方法版本的同类错误,比如调用了数字没有的方法。
- E0308:mismatched types:函数返回的是数字、你却以为是结构体时,它常常和 E0610 一起出现。
- 所有编译错误:错误页面的完整列表。
小测验
1. 写完 let total = cart.total;(total 是一个 u32 字段)之后,total.amount 会怎样?
2. 错误信息里的 {integer} 是什么意思?
3. let x = point.0; 之后,x.0 报了 E0610。怎么修?
得分: 0 / 3
- E0610 — Rust error codes index(英文):这个错误的官方说明。
- 数据类型 — Rust 程序设计语言:标量类型(整数、浮点数、
bool、char)和复合类型(元组、数组)。 - 使用元组结构体创建不同的类型 — Rust 程序设计语言:
Meters(u32)这样的包装类型,以及.0在它们身上怎样工作。 - 原生类型 — 通过例子学 Rust:语言内置的类型,附有可运行的示例。
i32— 标准库文档(英文):整数拥有的所有方法(你会发现它们都要用()调用)。