Bỏ qua để đến nội dung

Lỗi Rust E0027: pattern does not mention field X (mẫu bỏ sót trường)

E0027 nghĩa là: bạn đang tách một struct hoặc biến thể enum ra bằng một mẫu (pattern), như let Order { item, quantity } = order;, và bạn chưa nhắc tới mọi trường. Trong Rust, một mẫu struct phải tính tới tất cả các trường, hoặc bằng cách ghi tên chúng, hoặc bằng cách nói “và phần còn lại” với ... Đây là một trong những lỗi dễ chịu nhất: rustc in ra chính xác cách sửa, và gần như lúc nào cũng chỉ là hai dấu chấm. (Trang này thuộc loạt bài Giải thích các lỗi trình biên dịch 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
| ++++

Thông báo của trình biên dịch luôn bằng tiếng Anh: “pattern does not mention fields id, paid” nghĩa là “mẫu không nhắc tới các trường id, paid”. Đây là chương trình gây ra lỗi đó (chú thích được giữ bằng tiếng Anh vì nó xuất hiện nguyên văn trong thông báo lỗi):

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);
}

Dòng tiêu đề liệt kê các trường mà mẫu của bạn bỏ sót (id và paid). Dấu ^^^ gạch dưới toàn bộ mẫu, vì vấn đề không nằm ở một từ viết sai, mà là có thứ gì đó bị thiếu.

Sau đó rustc đưa ra ba gợi ý help:. Cả ba đều hợp lệ, và chúng mang ý nghĩa khác nhau:

  • include the missing fields (thêm các trường còn thiếu): ghi tên chúng ra, và bạn có thêm biến id và paid.
  • explicitly ignore them (bỏ qua chúng một cách tường minh): viết id: _, paid: _. Bạn không có biến nào, nhưng bạn đã liệt kê mọi trường theo tên.
  • always ignore missing fields here (luôn bỏ qua các trường thiếu ở đây): viết ... Nghĩa là “và bất kỳ trường nào khác, tôi không quan tâm”.

Giống như một tờ danh sách kiểm tra ở cửa ghi rằng “hãy đánh dấu mọi ô, hoặc đánh dấu ô Khác: bỏ qua”. Bạn không thể cứ để trống các ô.

Một mẫu struct trong Rust mặc định phải đầy đủ, trừ khi bạn nói khác. Điều này làm ý định của bạn rõ ràng với người đọc sau. Order { item, quantity, .. } nói “tôi chỉ cần hai trường này”. Order { id, item, quantity, paid } nói “tôi đã cân nhắc từng trường một”. Xem Bỏ qua một phần với _ và ...

Lợi ích thật sự đến khi struct thay đổi. Nếu ai đó thêm trường discount vào Order, một mẫu liệt kê mọi trường theo tên sẽ hỏng với E0027, chỉ cho bạn tới đoạn code có thể cần xử lý trường mới. Một mẫu có .. thì vẫn biên dịch bình thường. Rust để bạn chọn cách hành xử mình muốn, nhưng nó sẽ không đoán thay bạn.

Cách sửa 1: Thêm .. để bỏ qua phần còn lại

Phần tiêu đề “Cách sửa 1: Thêm .. để bỏ qua phần còn lại”

Phần lớn trường hợp, hãy chọn cách này. Nếu bạn chỉ cần một vài trường, hãy kết thúc mẫu bằng ... Nó dùng được trong let, match, if let và tham số hàm, và phải đứng cuối cùng.

struct Order {
id: u32,
item: String,
quantity: u32,
paid: bool,
}
fn main() {
let order = Order { id: 1, item: String::from("bàn phím"), quantity: 2, paid: true };
let Order { item, quantity, .. } = order; // ✅ `..` bỏ qua `id` và `paid`
println!("{} x {}", quantity, item);
let orders = [
Order { id: 2, item: String::from("chuột"), quantity: 1, paid: false },
Order { id: 3, item: String::from("màn hình"), quantity: 1, paid: true },
];
for order in &orders {
match order {
Order { paid: true, item, .. } => println!("giao {}", item),
Order { id, .. } => println!("đơn hàng #{} đang chờ thanh toán", id),
}
}
}

Kết quả:

2 x bàn phím
đơn hàng #2 đang chờ thanh toán
giao màn hình
▶ Tự chạy thử

Hãy để ý paid: true ở nhánh đầu tiên: một trường trong mẫu cũng có thể được so với một giá trị, và .. vẫn lo phần các trường còn lại.

Cách sửa 2: Ghi tên trường còn thiếu, hoặc bỏ qua nó theo tên bằng _

Phần tiêu đề “Cách sửa 2: Ghi tên trường còn thiếu, hoặc bỏ qua nó theo tên bằng _”

Chọn cách này khi bạn thật sự cần trường đó, hoặc khi bạn muốn mẫu hỏng nếu sau này có trường mới được thêm vào. Cùng lỗi này xuất hiện trong một match trên biến thể enum có trường được đặt tên:

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`

Chương trình đằng sau lỗi nà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"),
}
}

Một lần di chuyển có hai tọa độ, và code thật ra nên dùng cả hai. Vậy hãy ghi tên y. Khi một trường thật sự không quan trọng, y: _ nói rõ điều đó ra, mà vẫn liệt kê nó theo tên:

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!("trượt dọc sàn tới x = {}", x),
Message::Move { x, y } => println!("di chuyển tới ({}, {})", x, y), // ✅ cả hai trường
Message::Quit => println!("thoát"),
}
}
// mọi trường đều được ghi tên: thêm một trường vào `Move` sẽ cố ý làm hỏng chỗ này
if let Message::Move { x, y: _ } = &messages[0] {
println!("x đầu tiên = {}", x);
}
}

Kết quả:

di chuyển tới (1, 2)
trượt dọc sàn tới x = 5
thoát
x đầu tiên = 1
▶ Tự chạy thử

Nếu bạn ghi tên một trường chỉ để bỏ qua nó, hãy viết y: _, đừng viết y trơn. Một biến y trơn mà bạn không bao giờ dùng sẽ gây ra cảnh báo “unused variable” (biến không được dùng).

Chọn cách này khi E0027 xuất hiện cùng với E0026 trên cùng một dòng. Ví dụ, Shape::Circle { r } cho một biến thể có trường tên radius sẽ gây ra cả hai: E0026 cho r (không có trường này) và E0027 cho radius (không được nhắc tới). Gợi ý của rustc cho E0027 sẽ đề xuất thêm radius bên cạnh r, nhưng đó không phải điều bạn muốn. Cách sửa thật sự là thay cái tên sai: Shape::Circle { radius }, hoặc Shape::Circle { radius: r } để giữ tên biến ngắn. Khi đó cả hai lỗi biến mất cùng lúc. Xem các ví dụ ở trang E0026.

Sau khi thêm một trường vào struct. Bạn thêm created_at vào Order, và mọi mẫu từng liệt kê đủ các trường giờ đều báo E0027. Đó chính là bước kiểm tra đang làm đúng việc của nó. Hãy quyết định cho từng chỗ: đoạn code này có cần created_at không? Nếu có, hãy ghi tên nó; nếu không, hãy thêm ...

Tham số hàm. fn print_order(&Order { item, quantity }: &Order) cũng báo E0027. Cách sửa: &Order { item, quantity, .. }. Xem Mẫu trong let và tham số hàm.

Tuple struct và biến thể tuple. Với struct Color(u8, u8, u8);, mẫu Color(r) cũng chưa đầy đủ, nhưng rustc báo nó là E0023, “this pattern has 1 field, but the corresponding tuple struct has 3 fields” (mẫu này có 1 trường, nhưng tuple struct tương ứng có 3 trường). Cách sửa cùng một ý tưởng: Color(r, ..) hoặc Color(r, _, _).

Kiểm tra nhanh

1. struct Point { x: i32, y: i32, z: i32 }. Mẫu nào biên dịch được và chỉ cho bạn x?

2. Order { item, .. } và Order { item, id: _, quantity: _, paid: _ } khác nhau ở đâu?

3. Bạn gặp E0026 cho r và E0027 cho radius trên cùng một dòng. Cách sửa tốt nhất là gì?

Điểm: 0 / 3