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.)
Thông báo lỗi
Phần tiêu đề “Thông báo lỗi”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);}Ý nghĩa
Phần tiêu đề “Ý nghĩa”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ếnidvàpaid.explicitly ignore them(bỏ qua chúng một cách tường minh): viếtid: _, 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 ô.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”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
Phần tiêu đề “Cách sửa”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ángiao màn hìnhHã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 = 5thoátx đầu tiên = 1Nế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).
Cách sửa 3: Sửa tên trường bị gõ sai
Phần tiêu đề “Cách sửa 3: Sửa tên trường bị gõ sai”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.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”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, _, _).
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0026: struct or variant does not have a field named x: một tên trường trong mẫu không tồn tại. Thường xuất hiện cùng E0027.
- E0063: missing field in initializer: cùng ý tưởng khi tạo một struct: mọi trường đều cần một giá trị (hoặc
..Default::default()). - E0004: non-exhaustive patterns: một
matchkhông bao quát mọi biến thể, trong khi E0027 nói về mọi trường. E0023: this pattern has N fields, but the corresponding tuple struct has M fields: phiên bản dành cho tuple struct (xem ở trên).- Tất cả lỗi trình biên dịch
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”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
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0027 — Rust error codes index (tiếng Anh): phần giải thích chính thức của lỗi này.
- Pattern Syntax — The Rust Book (tiếng Anh): phân rã struct và mục “Ignoring Remaining Parts of a Value with
..”. - Destructuring structs — Rust by Example (tiếng Anh): các mẫu struct chạy được, có dùng
... - Patterns — The Rust Reference (tiếng Anh): các quy tắc chính xác cho mẫu struct và mẫu “phần còn lại” (rest pattern).