Lỗi Rust E0610: primitive type doesn't have fields (kiểu nguyên thủy không có field)
E0610 nghĩa là: bạn viết something.field, nhưng something là một con số đơn thuần (hoặc một bool hay char), và những kiểu này hoàn toàn không có field (trường) nào. Lỗi này gần như luôn có nghĩa là biến không phải thứ bạn nghĩ. Có thể bạn đã lấy con số ra khỏi struct từ trước, một hàm trả về con số thay vì struct, hoặc bạn viết thừa một .0. Thông báo cho bạn biết chính xác kiểu mà Rust nhìn thấy, nên bạn chỉ còn cách cách sửa một bước. (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[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> src/main.rs:3:22 |3 | println!("{}", x.value); // ❌ x is a number, it has no `value` field | ^^^^^Trình biên dịch nói rằng {integer} là một kiểu nguyên thủy (primitive type) nên không có field nào. Đây là chương trình gây ra lỗi:
fn main() { let x = 5; println!("{}", x.value); // ❌ x is a number, it has no `value` field}Ý nghĩa
Phần tiêu đề “Ý nghĩa”Kiểu nằm trong dấu backtick là thứ Rust nghĩ biến của bạn đang có. Ở đây là `{integer}`, nghĩa là “một số nguyên nào đó, tôi chưa quyết định là loại nào” (Rust sẽ chọn i32 sau nếu không có gì khác quyết định). Bạn cũng sẽ gặp `{float}` cho số thập phân, hoặc một kiểu cụ thể như u32, usize, f64, bool hay char.
Dấu ^^^^^ chỉ vào cái tên sau dấu chấm. Rust không nói “value bị viết sai chính tả”. Nó đang nói “một con số không thể có bất kỳ field nào, dù bạn gọi nó là gì”.
Vậy câu hỏi cần đặt ra là: vì sao biến này lại là một con số? Giống như hỏi “tem giá dán trên số 5”. Một món hàng có tem giá. Số 5 chỉ là một con số.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”Các kiểu nguyên thủy như i32, u32, f64, bool và char là những giá trị đơn được xây sẵn trong ngôn ngữ. Chúng không phải struct, nên bên trong chẳng có gì để bạn với tới bằng dấu chấm và một cái tên. Xem Kiểu dữ liệu.
Tuy vậy, chúng có phương thức (method): x.abs(), x.pow(2), price.round(), c.is_alphabetic(). Phương thức được gọi với (). Field là dữ liệu lưu bên trong một struct, và chỉ struct, tuple và union mới có.
Vì Rust kiểm tra mọi .field lúc biên dịch, sự nhầm lẫn này không bao giờ trở thành một lần sập chương trình hay một undefined âm thầm lúc chạy. Bạn biết ngay, đúng tại dòng đó.
Cách sửa
Phần tiêu đề “Cách sửa”Cách sửa 1: Dùng thẳng con số
Phần tiêu đề “Cách sửa 1: Dùng thẳng con số”Chọn cách này khi bạn đã lấy field ra khỏi struct từ trước. Sau let count = counter.value;, count chính là con số, nên count.value là đòi một field trên một u32:
error[E0610]: `u32` is a primitive type and therefore doesn't have fields --> src/main.rs:8:26 |8 | println!("{}", count.value); | ^^^^^Hoặc dùng con số như nó vốn là, hoặc quay lại struct và đọc field từ đó:
struct Counter { value: u32,}
fn main() { let mut counter = Counter { value: 0 }; counter.value += 3;
let count = counter.value; // giờ count là một u32 đơn thuần println!("{}", count); // ✅ dùng chính con số println!("{}", counter.value); // ✅ hoặc đọc field từ struct}Kết quả:
33Không chắc một biến có kiểu gì? Hãy rê chuột lên nó trong editor (với rust-analyzer, xem Công cụ Rust). Hoặc tạm viết let _: () = count;: rustc sẽ trả lời bằng expected `()`, found `usize` và cho bạn biết kiểu thật. Xóa dòng đó đi sau khi xong.
Cách sửa 2: Trả về hoặc lưu struct, không phải con số
Phần tiêu đề “Cách sửa 2: Trả về hoặc lưu struct, không phải con số”Chọn cách này khi giá trị đến từ một hàm hay một collection, và bạn tưởng nó là struct. Hai trường hợp phổ biến:
- Một hàm như
fn find_order() -> u32trả về id của đơn hàng, rồiorder.totallỗi vìorderlà mộtu32. - Một
HashMap<&str, u32>chỉ lưu điểm số, rồiscores["ana"].scorelỗi vì mỗi giá trị là mộtu32, không phải mộtPlayer.
Hãy quyết định bạn thật sự muốn gì. Nếu bạn cần nhiều mẩu dữ liệu, hãy đổi kiểu trả về hoặc kiểu giá trị thành struct:
use std::collections::HashMap;
struct Order { id: u32, total: u32, // tính bằng xu}
// trả về cả Order, không chỉ một con sốfn new_order(id: u32, total: u32) -> Order { Order { id, total }}
struct Player { score: u32, level: u8,}
fn main() { let order = new_order(7, 1999); println!("Đơn hàng {} có giá {} xu", order.id, order.total);
// map lưu cả Player, không chỉ điểm của họ let mut players: HashMap<&str, Player> = HashMap::new(); players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"]; println!("ana: điểm {}, cấp {}", ana.score, ana.level);}Kết quả:
Đơn hàng 7 có giá 1999 xuana: điểm 10, cấp 2Nếu bạn chỉ cần một con số, hãy giữ u32 và bỏ phần .field đi. Xem Struct và HashMap.
Cách sửa 3: Bỏ .0 thừa
Phần tiêu đề “Cách sửa 3: Bỏ .0 thừa”Chọn cách này khi lỗi chỉ vào một .0 hoặc .1. Tuple và tuple struct dùng vị trí cho field, và rất dễ đi sâu quá một tầng. Sau let x = point.0;, x đã là con số rồi, nên x.0 cho ra `{integer}` is a primitive type. Chuyện tương tự xảy ra với một kiểu bọc như struct Meters(u32);: một .0 lấy u32 ra, và .0 thứ hai sẽ lỗi.
struct Meters(u32);
fn main() { let point = (3, 4); let x = point.0; // x đã là con số 3 println!("x = {}", x); // không phải x.0
let m = Meters(42); println!("{} m", m.0); // một .0 lấy u32 ra khỏi tuple struct
let line = ((0, 0), (3, 4)); // tuple lồng nhau: mỗi tầng một vị trí println!("y của điểm cuối = {}", line.1.1);}Kết quả:
x = 342 my của điểm cuối = 4Hãy đếm số tầng: mỗi .0 hoặc .1 đi vào một tầng của tuple. Khi đã tới con số thì dừng. Nếu các vị trí bắt đầu gây rối, hãy đặt tên cho chúng bằng một struct. Xem Truy cập phần tử theo vị trí và Khi nào dùng tuple, khi nào dùng struct.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”Một phương thức thiếu (). price.round trên một f64, hay x.abs trên một i32, không phải E0610 mà là E0615, “attempted to take value of method”, vì những cái tên đó là phương thức. Cách sửa: price.round(), x.abs(). Một điều bất ngờ: nếu kiểu vẫn là {integer} (như trong let x = -5;), thì ngay cả x.abs cũng cho ra E0610, và x.abs() khi đó cho ra E0689 “can’t call method abs on ambiguous numeric type {integer}”. Hãy chọn một kiểu, let x: i32 = -5;, là x.abs() chạy được.
Một tham chiếu đến con số. Nếu biến là &u32 thay vì u32, mã lỗi đổi thành E0609: no field `value` on type `&u32`. Chỗ hay gặp là một vòng lặp như for (name, score) in &players, trong đó score là &i32, và score.1 cho ra no field `1` on type `&{integer}`. Cùng nguyên nhân, cùng cách sửa: score đã là con số rồi.
Kết quả của len() hay count(). let len = scores.len(); cho bạn một usize, nên len.count lỗi với `usize` is a primitive type. Cách sửa: dùng thẳng len.
Số có đơn vị. Nếu bạn muốn price.cents hay distance.meters, hãy tạo một struct nhỏ: struct Price { cents: u64 }. Khi đó price.cents chạy được, và kiểu sẽ ngăn bạn nhầm lẫn giá tiền với những con số khác. Xem Struct.
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0609: no field
xon typeT: kiểu không có field tên đó. Bạn nhận lỗi này (chứ không phải E0610) với tham chiếu như&u32. - E0615: attempted to take value of method: bạn viết
price.roundthay vìprice.round(). E0689: can't call method on ambiguous numeric type: gọi phương thức trên một con số chưa được chọn kiểu. Hãy thêm một kiểu nhưi32.- E0599: no method named x found: phiên bản “phương thức”, ví dụ một phương thức mà số không có.
- E0308: mismatched types: hay xuất hiện cạnh E0610 khi một hàm trả về con số mà bạn lại tưởng là struct.
- Giải thích tất cả cá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. Sau let total = cart.total; (trong đó total là một field u32), total.amount cho ra gì?
2. {integer} trong thông báo nghĩa là gì?
3. let x = point.0; rồi x.0 cho ra E0610. Cách sửa là gì?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0610 — Rust error codes index (tiếng Anh): giải thích chính thức về lỗi này.
- Data Types — The Rust Book (tiếng Anh): kiểu vô hướng (số nguyên, số thực,
bool,char) và kiểu phức hợp (tuple, mảng). - Tuple Structs — The Rust Book (tiếng Anh): các kiểu bọc như
Meters(u32)và cách.0hoạt động trên chúng. - Primitives — Rust by Example (tiếng Anh): các kiểu có sẵn, kèm ví dụ chạy được.
i32— thư viện chuẩn Rust (tiếng Anh): mọi phương thức của một số nguyên (và bạn sẽ thấy tất cả đều được gọi với()).