跳转到内容

Rust 错误 E0026:struct or variant does not have a field named x(模式中的字段名写错)

E0026 的意思是:你在模式里拆解一个结构体或枚举变体,比如 Shape::Circle { r },而你写的某个字段名根本不存在。它是 E0560 和 E0559 的模式版本,那两个错误在你创建值时抓的是同一种错误。rustc 通常会在 help: 行里告诉你正确的名字,所以修复往往只需要改一个词。这个系列的其他错误,请看 Rust 编译错误详解。

error[E0026]: variant `Shape::Circle` does not have a field named `r`
--> src/main.rs:8:25
|
8 | Shape::Circle { r } => 3.14 * r * r, // ❌ the field is called `radius`
| ^
| |
| variant `Shape::Circle` does not have this field
| help: `Shape::Circle` has a field named `radius`

第一行的意思是“变体 Shape::Circle 没有名为 r 的字段”。help: 告诉你:Shape::Circle 有一个叫 radius 的字段。

产生这个错误的程序是这样的:

enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { r } => 3.14 * r * r, // ❌ the field is called `radius`
Shape::Rectangle { width, height } => width * height,
}
}
fn main() {
println!("{}", area(&Shape::Circle { radius: 1.0 }));
}

同一行你还会看到第二个错误:E0027,“pattern does not mention field radius”(模式没有提到字段 radius)。这是从另一面看到的同一个错误。Rust 发现了一个你写了但不存在的字段(r),还发现了一个真实存在、你却没写的字段(radius)。把名字改对,两个错误就都没了。

第一行写出了你正在匹配的结构体或变体(Shape::Circle),以及你写的字段(r)。^ 指向模式大括号里的这个名字。

在模式里,{ } 中的名字是字段名,而不是你可以随意起的新变量名。Shape::Circle { r } 是 Shape::Circle { r: r } 的简写,意思是“取出叫 r 的字段,放进叫 r 的变量里”。Circle 没有叫 r 的字段,所以 Rust 停了下来。

在箭头下面,rustc 会给出下面某一种提示:

  • help: `Shape::Circle` has a field named `radius`:正好只有一个字段你没提到,所以 rustc 猜你指的就是它。
  • help: a field with a similar name exists(有一个名字相近的字段):拼写错误,比如把 name 写成了 nmae。rustc 会给出改正后的那一行。
  • 完全没有提示:rustc 猜不出来。去看结构体或枚举的定义,找到真实的名字。

就像你请图书管理员去“那个叫‘差不多是小说’的书架”拿书。管理员知道你想要某个书架,但只能去真实存在的书架。

像 Shape::Circle { radius } 这样的模式是一个约定:“当这个值是 Circle 时,把它的 radius 给我”。Rust 在编译时就拿这个约定去对照定义,所以模式永远不会去要不存在的数据。参见解构元组和结构体。

在没有这种检查的语言里,读取一个不存在的键会得到 undefined 或 null,bug 要到很久以后、离拼错的地方很远的位置才冒出来。在 Rust 里,拼写错误过不了编译器这一关。有人给字段改名时,它也能保护你:每个还在用旧名字的模式都会亮起 E0026,一个都不会被悄悄漏掉。

大多数时候都选这个方法。用 help: 行里给出的名字,或者去看定义。字段名区分大小写,并且用 snake_case 书写。

拼写错误会得到“名字相近”的提示和改正后的那一行:

error[E0026]: struct `User` does not have a field named `nmae`
--> src/main.rs:8:16
|
8 | let User { nmae, age } = user; // ❌ typo: the field is `name`
| ^^^^ struct `User` does not have this field
|
help: a field with a similar name exists
|
8 - let User { nmae, age } = user; // ❌ typo: the field is `name`
8 + let User { name, age } = user; // ❌ typo: the field is `name`
|

背后的程序:

struct User {
name: String,
age: u32,
}
fn main() {
let user = User { name: String::from("Ferris"), age: 7 };
let User { nmae, age } = user; // ❌ typo: the field is `name`
println!("{} is {} years old", nmae, age);
}

完全按照定义来写字段名。这样简写就能用,你会得到同名的变量:

struct User {
name: String,
age: u32,
}
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius } => 3.14 * radius * radius, // ✅ 真实的字段名
Shape::Rectangle { width, height } => width * height,
}
}
fn main() {
let shapes = [
Shape::Circle { radius: 2.0 },
Shape::Rectangle { width: 3.0, height: 4.0 },
];
for shape in &shapes {
println!("面积:{:.2}", area(shape));
}
let user = User { name: String::from("Ferris"), age: 7 };
let User { name, age } = user; // ✅ 是 `name`,不是 `nmae`
println!("{} 今年 {} 岁", name, age);
}

输出:

面积:12.56
面积:12.00
Ferris 今年 7 岁
▶ 动手试一试

修复 2:用 字段: 变量名 保留你自己的变量名

Section titled “修复 2:用 字段: 变量名 保留你自己的变量名”

如果你就是喜欢短名字 r,只想让变量叫这个名字,选这个方法。先写真实的字段名,再写冒号,然后写你的变量名:Shape::Circle { radius: r }。读作“取出 radius 字段,把它叫作 r”。

enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius: r } => 3.14 * r * r, // ✅ 字段 `radius`,变量 `r`
Shape::Rectangle { width: w, height: h } => w * h,
}
}
fn main() {
let small = Shape::Circle { radius: 1.0 };
let wide = Shape::Rectangle { width: 10.0, height: 2.5 };
println!("小圆:{:.2}", area(&small));
println!("宽矩形:{:.2}", area(&wide));
}

输出:

小圆:3.14
宽矩形:25.00
▶ 动手试一试

当字段名和你已有的变量重名,或者字段名很长时,这种写法也很方便。

修复 3:确认你匹配的是正确的变体

Section titled “修复 3:确认你匹配的是正确的变体”

当你写的字段确实存在,只是属于另一个变体时,选这个方法。枚举的每个变体都有自己的字段。这里 x 属于 Click,但这个分支匹配的是 KeyPress:

error[E0026]: variant `Event::KeyPress` does not have a field named `x`
--> src/main.rs:9:27
|
9 | Event::KeyPress { x, .. } => format!("key at {}", x), // ❌ `x` belongs to `Click`
| ^
| |
| variant `Event::KeyPress` does not have this field
| help: `Event::KeyPress` has a field named `key`

背后的程序:

enum Event {
Click { x: i32, y: i32 },
KeyPress { key: char },
}
fn describe(event: &Event) -> String {
match event {
Event::Click { x, y } => format!("click at ({}, {})", x, y),
Event::KeyPress { x, .. } => format!("key at {}", x), // ❌ `x` belongs to `Click`
}
}
fn main() {
println!("{}", describe(&Event::KeyPress { key: 'a' }));
}

rustc 猜的 key 只是“你唯一没提到的字段”,别不假思索地照搬。先问问自己这个分支应该做什么。按键没有位置,所以这里正确的修法是用 KeyPress 真正拥有的字段:

enum Event {
Click { x: i32, y: i32 },
KeyPress { key: char },
}
fn describe(event: &Event) -> String {
match event {
Event::Click { x, y } => format!("点击位置:({}, {})", x, y),
Event::KeyPress { key } => format!("按下了 '{}' 键", key), // ✅ KeyPress 有 `key`
}
}
fn main() {
let events = [Event::Click { x: 10, y: 20 }, Event::KeyPress { key: 'a' }];
for event in &events {
println!("{}", describe(event));
}
}

输出:

点击位置:(10, 20)
按下了 'a' 键
▶ 动手试一试

如果按键确实应该有位置,那就给变体加上这些字段:KeyPress { key: char, x: i32, y: i32 }。这样一来,所有创建 KeyPress 的地方也都必须填上它们(rustc 会用 E0063 逐一指出)。

给字段改名之后。 你在枚举里把 r 改成了 radius,结果每个 Shape::Circle { r } => ... 分支都报 E0026。在项目里搜索旧名字,逐个更新模式;或者写成 radius: r,保留原来的变量名。

在 if let 和 let 里。 if let Some(User { nmae, .. }) = find_user() 也会报同样的错误。这里加 .. 没用:.. 跳过的是你没写的字段,而你写了的每个名字都必须存在。修复:User { name, .. }。

给元组变体或元组结构体写了大括号。 对于 struct Point(i32, i32);,模式 Point { x, y } 里的名字没有对应的真实字段。rustc 会用另一个代码 E0769 报告,“tuple variant Point written as struct variant”(元组变体 Point 被写成了结构体变体),并建议写成 Point(x, y)。参见解构带数据的枚举。

小测验

1. 对于 enum Shape { Circle { radius: f64 } },模式 Shape::Circle { r } 要的是什么?

2. 你想把 radius 字段放进叫 r 的变量里。哪个模式可以?

3. 如果字段叫 name,User { nmae, .. } 能编译吗?

得分: 0 / 3