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` |第一行的意思是“结构体 User 没有名为 nmae 的字段”。help: 告诉你“有一个名字相近的字段”,并给出了改好的那一行。
产生这个错误的程序是这样的:
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { nmae: String::from("Ferris"), // ❌ 拼写错误:字段名是 `name` active: true, }; println!("{}", user.email);}这个错误是什么意思
Section titled “这个错误是什么意思”第一行点出了结构体(User)和你写的字段(nmae)。^^^^ 指向 User { ... } 里的这个字段。Rust 拿它和 struct User 的字段列表对比了一遍,没有找到对得上的。
箭头下面会出现三种提示中的一种,每一种说明的情况都不一样:
help: a field with a similar name exists(有名字相近的字段):拼写错误。rustc 会给出写对名字的那一行。note: available fields are: `username`(可用的字段有……):没有相近的名字。列出来的是你还没填的字段。note: all struct fields are already assigned(所有字段都已经赋值了):真正的字段你都填了,这一个是多出来的。
注意,rustc 不会再另外抱怨缺少 name。改掉拼写错误,两个问题就一起解决了。
这就像填一张纸质表格时,把电话号码写进了一个叫“传真”的格子。表格上根本没有这个格子,于是工作人员把表格退了回来。
Rust 为什么要这样做
Section titled “Rust 为什么要这样做”结构体的字段在你写下 struct User { ... } 的那一刻就固定了。每次创建 User,Rust 都会检查你给的字段是不是恰好就是这些:不能少,不能多,也不能拼错。参见定义和创建结构体。
这能抓出真正的 bug。在 JavaScript 里,{ nmae: "Ferris" } 会悄悄地创建一个键名写错的对象,很久以后才在某个地方发现 user.name 是 undefined。在 Rust 里,拼写错误过不了编译器这一关,所以程序里的每个 User 都真的有一个 name。
修复 1:改正拼写
Section titled “修复 1:改正拼写”看到 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"), // ✅ 真正的字段名 active: true, }; println!("{}({})已启用:{}", user.name, user.email, user.active);
// 简写:变量名和字段名一致 let name = String::from("Ada"); let other = User { name, email, active: false }; println!("{}({})已启用:{}", other.name, other.email, other.active);}输出:
Ferris([email protected])已启用:trueAda([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`你有两个正当的选择。如果结构体是对的,就用它现在的字段名。如果你的代码是对的,结构体确实需要这个字段,就把它加进结构体的定义里。下面的 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"), }; println!("{}({})", user.username, user.email);
let config = Config { port: 8080, host: String::from("localhost"), debug: true, }; println!("{}:{} debug={}", config.host, config.port, config.debug);}输出:
ferris([email protected])localhost:8080 debug=true如果这个结构体来自你刚升级的某个 crate,字段可能在新版本里改了名。去 docs.rs(英文)上查看那个版本的结构体,或者看看 crate 的更新日志(changelog)。一旦给自己的结构体加了字段,每个创建它的地方也都得填上这个字段。rustc 会用 E0063 逐一指出这些地方(见下文)。
修复 3:用对结构体
Section titled “修复 3:用对结构体”当你写的字段确实存在,只是在另一个结构体里时,选这个方法。如果有两个相似的结构体,比如带 title 的 Product 和带 name 的 Customer,很容易把它们搞混:
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 那条 note。如果列出来的字段出乎你的意料,说明你在构建错误的结构体。换成正确的结构体名,或者用你本来想要的那个结构体的字段名:
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` }; println!("{} 买了 {},价格 {} 分", buyer.name, keyboard.title, keyboard.price); println!("收据已发送到 {}", buyer.email);}输出:
Ana 买了 Keyboard,价格 4900 分收据已发送到 [email protected]修复 4:元组结构体要用圆括号
Section titled “修复 4:元组结构体要用圆括号”当结构体是元组结构体(tuple struct),比如 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 }。参见什么时候用元组,什么时候用结构体。
反过来的情况:E0063 缺少字段
Section titled “反过来的情况:E0063 缺少字段”E0560 的“双胞胎”是 E0063:结构体有这个字段,但你没有填。Rust 从不替你补字段,所以每个字段都需要一个值:
error[E0063]: missing field `active` in initializer of `User` --> src/main.rs:8:16 |8 | let user = User { | ^^^^ missing `active`如果缺了好几个字段,它会全部列出来:missing fields `active`, `email` and `login_count` in initializer of `User`。修复:给每个字段都赋值。如果很多字段都有合理的默认值,就 derive 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。这时 note 会列出所有字段(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`,同样带有 available fields 的 note 和拼写提示。修复:Shape::Circle { radius: 2.0 }。参见枚举可以携带数据。
来自其他模块的结构体。 如果字段存在但不是 pub 的,你得到的会是 E0451:“field balance of struct Account is private”(字段是私有的)。修复:用模块提供的构造函数,比如 Account::new(...)。参见默认一切都是私有的。
- E0609:no field
xon typeT:同样是拼写错误,只不过发生在读取字段(user.nmae)时,而不是创建结构体时。 - E0451:field is private in struct literal:字段存在,但你在这里不能给它赋值。
- E0308:mismatched types:字段名是对的,但值的类型不对,比如给
String字段传了"Ferris"。 E0063: missing field in initializer:你漏掉了某个字段(见上文)。E0559: variant has no field named x:枚举变体版本的 E0560。- 所有编译错误:错误页面的完整列表。
小测验
1. rustc 说 note: all struct fields are already assigned。这是什么意思?
2. 你有 let nmae = ...;,然后写了 User { nmae, email }。怎么修?
3. 漏掉一个字段时,比如写了 User { name, email } 却没有 active,会得到哪个错误?
得分: 0 / 3
- E0560 — Rust error codes index(英文):这个错误的官方说明。
- 结构体的定义和实例化 — Rust 程序设计语言:结构体字面量、字段初始化简写、结构体更新语法和元组结构体。
- E0063 — Rust error codes index(英文):这个错误的“双胞胎”,也就是“缺少字段”的官方页面。
- Struct expressions — The Rust Reference(英文):
Name { field: value }、简写和..base的精确规则。 Default— 标准库文档(英文):#[derive(Default)]和..Default::default()是怎样工作的。