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

Lỗi Rust E0560: struct has no field named X (struct không có field này)

E0560 nghĩa là: bạn đang tạo một struct bằng Name { field: value }, và một trong các field (trường) bạn viết không tồn tại trong struct đó. Thường đó là lỗi gõ nhầm, một field đã bị đổi tên, hoặc một field thuộc về struct khác. rustc thường cho bạn biết chính xác cái tên bạn định viết, nên đây là một trong những lỗi sửa nhanh nhất. (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[E0560]: struct `User` has no field named `nmae`
--> src/main.rs:9:9
|
9 | nmae: String::from("Ferris"), // ❌ typo: the field is `name`
| ^^^^ unknown field
|
help: a field with a similar name exists
|
9 - nmae: String::from("Ferris"), // ❌ typo: the field is `name`
9 + name: String::from("Ferris"), // ❌ typo: the field is `name`
|

Trình biên dịch báo struct User không có field nào tên nmae và gợi ý field có tên gần giống là name. Đây là chương trình gây ra lỗi:

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
nmae: String::from("Ferris"), // ❌ typo: the field is `name`
email: String::from("[email protected]"),
active: true,
};
println!("{}", user.email);
}

Dòng tiêu đề nêu tên struct (User) và field bạn đã viết (nmae). Dấu ^^^^ chỉ vào field đó bên trong khối User { ... }. Rust đã so sánh nó với danh sách field trong struct User và không tìm thấy cái nào khớp.

Dưới mũi tên, bạn sẽ nhận một trong ba gợi ý. Mỗi gợi ý nói một điều khác nhau:

  • help: a field with a similar name exists: lỗi gõ nhầm. rustc cho thấy dòng code với tên đúng.
  • note: available fields are: `username`: không có tên nào gần giống. Đây là những field bạn chưa điền.
  • note: all struct fields are already assigned: bạn đã điền đủ mọi field thật, và field này là thừa.

Hãy để ý rằng rustc không phàn nàn thêm rằng name bị thiếu. Sửa lỗi gõ là cả hai vấn đề cùng biến mất.

Giống như điền một tờ đơn giấy và ghi số điện thoại vào ô “Fax”. Tờ đơn không có ô đó, nên nhân viên trả lại cho bạn.

Các field của một struct được cố định khi bạn viết struct User { ... }. Mỗi lần bạn tạo một User, Rust kiểm tra rằng bạn đưa đúng những field đó: không thiếu, không thừa, không sai chính tả. Xem Định nghĩa và tạo struct.

Điều này bắt được những bug thật. Trong JavaScript, { nmae: "Ferris" } âm thầm tạo ra một object với key sai, và user.name là undefined ở một chỗ nào đó rất lâu sau. Trong Rust, lỗi gõ không thể lọt qua trình biên dịch, nên mọi User trong chương trình của bạn thật sự có name.

Chọn cách này khi bạn thấy a field with a similar name exists. Dùng tên trong dòng help:. Tên field phân biệt chữ hoa chữ thường, và Rust dùng snake_case, nên userName và user_name là hai tên khác nhau.

Cũng hãy cẩn thận với cú pháp viết tắt khi khởi tạo field (field init shorthand): User { nmae, email } nghĩa là User { nmae: nmae, email: email }, nên tên biến phải trùng với tên field. Hoặc đổi tên biến, hoặc viết đầy đủ field: User { name: nmae, email }.

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
name: String::from("Ferris"), // ✅ tên field thật
email: String::from("[email protected]"),
active: true,
};
println!("{} ({}) đang hoạt động: {}", user.name, user.email, user.active);
// viết tắt: tên biến trùng với tên field
let name = String::from("Ada");
let email = String::from("[email protected]");
let other = User { name, email, active: false };
println!("{} ({}) đang hoạt động: {}", other.name, other.email, other.active);
}

Kết quả:

Ferris ([email protected]) đang hoạt động: true
Ada ([email protected]) đang hoạt động: false
▶ Tự chạy thử

Cách sửa 2: Dùng tên mới của field, hoặc thêm field

Phần tiêu đề “Cách sửa 2: Dùng tên mới của field, hoặc thêm field”

Chọn cách này khi không có gợi ý “similar name”. Thường là struct đã thay đổi: ai đó đổi name thành username, hoặc một field đã bị xóa, nhưng code này vẫn dùng cái cũ. rustc liệt kê những gì còn lại:

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`

Bạn có hai lựa chọn hợp lý. Nếu struct đúng, hãy dùng tên field hiện tại của nó. Nếu code của bạn đúng và struct thật sự cần field đó, hãy thêm nó vào định nghĩa struct. Ở đây Config báo all struct fields are already assigned cho debug, và cách sửa là thêm debug vào struct:

struct User {
username: String, // đổi tên từ `name`: hãy dùng tên mới
email: String,
}
struct Config {
port: u16,
host: String,
debug: bool, // thêm vào struct, vì code cần nó
}
fn main() {
let user = User {
username: String::from("ferris"),
email: String::from("[email protected]"),
};
println!("{} ({})", user.username, user.email);
let config = Config {
port: 8080,
host: String::from("localhost"),
debug: true,
};
println!("{}:{} debug={}", config.host, config.port, config.debug);
}

Kết quả:

localhost:8080 debug=true
▶ Tự chạy thử

Nếu struct đến từ một crate bạn vừa cập nhật, field có thể đã bị đổi tên trong phiên bản mới. Hãy xem struct đó trên docs.rs (tiếng Anh) cho phiên bản ấy, hoặc changelog của crate. Khi bạn thêm một field vào struct của mình, mọi chỗ tạo struct đó cũng phải điền nó. rustc sẽ chỉ ra từng chỗ bằng E0063 (xem bên dưới).

Chọn cách này khi field bạn viết có tồn tại, chỉ là ở một struct khác. Với hai struct giống nhau, như một Product có title và một Customer có name, rất dễ nhầm lẫn:

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`

Hãy nhìn ghi chú available fields. Nếu nó liệt kê những field bạn không mong đợi, bạn đang tạo nhầm struct. Hãy dùng đúng tên struct, hoặc tên field của struct bạn định dùng:

struct Product {
title: String,
price: u32, // tính bằng xu
}
struct Customer {
name: String,
email: String,
}
fn main() {
let keyboard = Product {
title: String::from("bàn phím"), // sản phẩm có `title`
price: 4900,
};
let buyer = Customer {
name: String::from("Ana"), // khách hàng có `name`
email: String::from("[email protected]"),
};
println!("{} mua một {} giá {} xu", buyer.name, keyboard.title, keyboard.price);
println!("Hóa đơn đã gửi đến {}", buyer.email);
}

Kết quả:

Ana mua một bàn phím giá 4900 xu
Hóa đơn đã gửi đến [email protected]
▶ Tự chạy thử

Cách sửa 4: Dùng dấu ngoặc tròn cho tuple struct

Phần tiêu đề “Cách sửa 4: Dùng dấu ngoặc tròn cho tuple struct”

Chọn cách này khi struct là một tuple struct, như struct Point(i32, i32);. Các field của nó không có tên, nên Point { x: 1, y: 2 } cho ra E0560 cho cả x lẫn y, kèm help: `Point` is a tuple struct, use the appropriate syntax và gợi ý Point(/* i32 */, /* i32 */). Hãy tạo nó bằng dấu ngoặc tròn, Point(1, 2), và đọc bằng .0 và .1. Nếu bạn thích có tên hơn, hãy đổi định nghĩa thành struct Point { x: i32, y: i32 }. Xem Khi nào dùng tuple, khi nào dùng struct.

Anh em sinh đôi của E0560 là E0063: một field mà struct có, nhưng bạn chưa điền. Rust không bao giờ tự điền field giúp bạn, nên field nào cũng cần một giá trị:

error[E0063]: missing field `active` in initializer of `User`
--> src/main.rs:8:16
|
8 | let user = User {
| ^^^^ missing `active`

Khi thiếu nhiều field, nó liệt kê tất cả: missing fields `active`, `email` and `login_count` in initializer of `User`. Cách sửa: cho mọi field một giá trị. Nếu nhiều field có giá trị mặc định hợp lý, hãy derive Default và dùng cú pháp cập nhật struct (struct update syntax) .. để điền phần còn lại:

#[derive(Debug, Default)]
struct Settings {
volume: u8,
dark_mode: bool,
language: String,
}
fn main() {
// viết ra đủ mọi field
let full = Settings { volume: 80, dark_mode: false, language: String::from("en") };
// chỉ những field bạn quan tâm, phần còn lại lấy từ Default
let quick = Settings { dark_mode: true, ..Default::default() };
// sao chép phần còn lại từ một giá trị khác
let louder = Settings { volume: 100, ..full };
println!("{:?}", quick);
println!("{:?}", louder);
}

Kết quả:

Settings { volume: 0, dark_mode: true, language: "" }
Settings { volume: 100, dark_mode: false, language: "en" }
▶ Tự chạy thử

Giá trị mặc định của một số là 0, của bool là false, và của String là chuỗi rỗng. Nếu bạn muốn giá trị mặc định khác, hãy tự viết impl Default for Settings. Xem Default: một giá trị khởi đầu hợp lý.

Bên trong cú pháp cập nhật ... Settings { dark: true, ..defaults } cũng cho ra E0560. Ở đây ghi chú liệt kê tất cả các field (available fields are: `volume`, `dark_mode`, `language`), và có thể không có gợi ý “similar name” khi các tên quá khác nhau. Cách sửa: dark_mode: true.

Biến thể enum có field được đặt tên. Với enum Shape { Circle { radius: f64 } }, viết Shape::Circle { r: 2.0 } là cùng một lỗi, nhưng rustc dùng mã khác: E0559, variant `Shape::Circle` has no field named `r`, cũng với ghi chú available fields và gợi ý lỗi gõ. Cách sửa: Shape::Circle { radius: 2.0 }. Xem Enum có thể chứa dữ liệu.

Một struct từ module khác. Nếu field có tồn tại nhưng không pub, bạn sẽ nhận E0451, “field balance of struct Account is private”. Cách sửa: dùng hàm khởi tạo của module, như Account::new(...). Xem Mọi thứ mặc định là riêng tư.

Kiểm tra nhanh

1. rustc nói note: all struct fields are already assigned. Điều đó nghĩa là gì?

2. Bạn có let nmae = ...; và viết User { nmae, email }. Sửa thế nào?

3. Bạn nhận lỗi nào khi bỏ sót một field, như User { name, email } mà không có active?

Điểm: 0 / 3