跳转到内容

Rust 错误 E0027:pattern does not mention field x(模式漏写字段)

E0027 的意思是:你用模式拆解一个结构体或枚举变体,比如 let Order { item, quantity } = order;,却没有提到所有字段。在 Rust 里,结构体模式必须交代清楚每一个字段:要么写出它们的名字,要么用 .. 表示“其余的都不要”。这是最友好的错误之一:rustc 会直接打印出修法,而且几乎总是加两个点就行。这个系列的其他错误,请看 Rust 编译错误详解。

error[E0027]: pattern does not mention fields `id`, `paid`
--> src/main.rs:10:9
|
10 | let Order { item, quantity } = order; // ❌ `id` and `paid` are not mentioned
| ^^^^^^^^^^^^^^^^^^^^^^^^ missing fields `id`, `paid`
|
help: include the missing fields in the pattern
|
10 | let Order { item, quantity, id, paid } = order; // ❌ `id` and `paid` are not mentioned
| ++++++++++
help: if you don't care about these missing fields, you can explicitly ignore them
|
10 | let Order { item, quantity, id: _, paid: _ } = order; // ❌ `id` and `paid` are not mentioned
| ++++++++++++++++
help: or always ignore missing fields here
|
10 | let Order { item, quantity, .. } = order; // ❌ `id` and `paid` are not mentioned
| ++++

第一行的意思是“模式没有提到字段 id 和 paid”。

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

struct Order {
id: u32,
item: String,
quantity: u32,
paid: bool,
}
fn main() {
let order = Order { id: 1, item: String::from("keyboard"), quantity: 2, paid: true };
let Order { item, quantity } = order; // ❌ `id` and `paid` are not mentioned
println!("{} x {}", quantity, item);
}

第一行列出了模式漏掉的字段(id 和 paid)。^^^ 划出了整个模式,因为问题不在于某个词写错了,而在于少了东西。

接着 rustc 给出三条 help: 建议。它们都是对的,但意思各不相同:

  • include the missing fields(把缺少的字段写进模式):写出它们的名字,这样你也会得到变量 id 和 paid。
  • explicitly ignore them(明确地忽略它们):写成 id: _, paid: _。你得不到变量,但每个字段都按名字列出来了。
  • always ignore missing fields here(在这里总是忽略缺少的字段):写 ..。意思是“还有其他字段,我不关心”。

就像门口的一张核对清单,上面写着“每一格都要打勾,或者勾选‘其他:跳过’”。你不能干脆留着空格不管。

在 Rust 里,结构体模式默认应该是完整的,除非你另外说明。这能让下一个读代码的人看清你的意图。Order { item, quantity, .. } 说的是“我只需要这两个”。Order { id, item, quantity, paid } 说的是“每个字段我都考虑过了”。参见用 _ 和 .. 忽略一部分。

真正的好处在结构体发生变化时才显现。如果有人给 Order 加了一个 discount 字段,按名字列出所有字段的模式就会报 E0027,把你带到可能需要处理新字段的代码那里。而带 .. 的模式会照常编译。Rust 让你自己选择想要哪种行为,但不会替你去猜。

大多数时候都选这个方法。如果你只需要部分字段,就在模式末尾加上 ..。它可以用在 let、match、if let 和函数参数里,而且必须放在最后。

struct Order {
id: u32,
item: String,
quantity: u32,
paid: bool,
}
fn main() {
let order = Order { id: 1, item: String::from("键盘"), quantity: 2, paid: true };
let Order { item, quantity, .. } = order; // ✅ `..` 跳过了 `id` 和 `paid`
println!("{} 个{}", quantity, item);
let orders = [
Order { id: 2, item: String::from("鼠标"), quantity: 1, paid: false },
Order { id: 3, item: String::from("显示器"), quantity: 1, paid: true },
];
for order in &orders {
match order {
Order { paid: true, item, .. } => println!("发货:{}", item),
Order { id, .. } => println!("订单 #{} 等待付款", id),
}
}
}

输出:

2 个键盘
订单 #2 等待付款
发货:显示器
▶ 动手试一试

注意第一个分支里的 paid: true:模式里的字段也可以拿来和一个值比较,.. 照样会处理其余字段。

修复 2:写出缺少的字段,或者用 _ 按名字忽略它

Section titled “修复 2:写出缺少的字段,或者用 _ 按名字忽略它”

当你确实需要这个字段,或者希望以后加字段时模式会报错,选这个方法。对带命名字段的枚举变体做 match,也会出现同样的错误:

error[E0027]: pattern does not mention field `y`
--> src/main.rs:9:9
|
9 | Message::Move { x } => println!("move to x = {}", x), // ❌ `y` is not mentioned
| ^^^^^^^^^^^^^^^^^^^ missing field `y`

背后的程序:

enum Message {
Move { x: i32, y: i32 },
Quit,
}
fn main() {
let m = Message::Move { x: 1, y: 2 };
match m {
Message::Move { x } => println!("move to x = {}", x), // ❌ `y` is not mentioned
Message::Quit => println!("quit"),
}
}

移动有两个坐标,代码其实应该两个都用上。所以把 y 写出来。如果某个字段确实无关紧要,y: _ 能把这一点明明白白地说出来,同时仍然按名字列出了它:

enum Message {
Move { x: i32, y: i32 },
Quit,
}
fn main() {
let messages = [Message::Move { x: 1, y: 2 }, Message::Move { x: 5, y: 0 }, Message::Quit];
for m in &messages {
match m {
Message::Move { x, y: 0 } => println!("沿着地面滑到 x = {}", x),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y), // ✅ 两个字段都有
Message::Quit => println!("退出"),
}
}
// 每个字段都按名字列出:给 `Move` 加字段时,这里会故意报错
if let Message::Move { x, y: _ } = &messages[0] {
println!("第一个 x = {}", x);
}
}

输出:

移动到 (1, 2)
沿着地面滑到 x = 5
退出
第一个 x = 1
▶ 动手试一试

如果你写出一个字段只是为了忽略它,请写 y: _,而不是单独一个 y。单独写一个从不使用的 y,会得到“unused variable”(未使用的变量)警告。

当 E0027 和 E0026 一起出现在同一行时,选这个方法。比如变体的字段叫 radius,你却写了 Shape::Circle { r },就会同时得到两个错误:r 报 E0026(没有这个字段),radius 报 E0027(没有提到)。rustc 在 E0027 的 help 里会建议你在 r 旁边加上 radius,但那并不是你想要的。真正的修法是把错的名字换掉:写成 Shape::Circle { radius },或者写成 Shape::Circle { radius: r } 保留短变量名。这样两个错误会一起消失。示例见 E0026 页面。

给结构体加字段之后。 你给 Order 加了 created_at,于是每个列出了所有字段的模式都报 E0027。这正是检查在发挥作用。逐个判断:这段代码需要 created_at 吗?需要就写出它,不需要就加 ..。

函数参数。 fn print_order(&Order { item, quantity }: &Order) 同样会报 E0027。修复:&Order { item, quantity, .. }。参见在 let 和函数参数里使用模式。

元组结构体和元组变体。 对于 struct Color(u8, u8, u8);,模式 Color(r) 也是不完整的,但 rustc 报的是 E0023:“this pattern has 1 field, but the corresponding tuple struct has 3 fields”(这个模式有 1 个字段,而对应的元组结构体有 3 个字段)。修法是同一个思路:Color(r, ..) 或 Color(r, _, _)。

小测验

1. struct Point { x: i32, y: i32, z: i32 }。哪个模式能编译,并且只取出 x?

2. Order { item, .. } 和 Order { item, id: _, quantity: _, paid: _ } 有什么区别?

3. 同一行上,r 报了 E0026,radius 报了 E0027。最好的修法是什么?

得分: 0 / 3