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

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.)

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
}

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ố.

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 đó.

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ả:

3
3
▶ Tự chạy thử

Khô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() -> u32 trả về id của đơn hàng, rồi order.total lỗi vì order là một u32.
  • Một HashMap<&str, u32> chỉ lưu điểm số, rồi scores["ana"].score lỗi vì mỗi giá trị là một u32, không phải một Player.

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 xu
ana: điểm 10, cấp 2
▶ Tự chạy thử

Nếu bạn chỉ cần một con số, hãy giữ u32 và bỏ phần .field đi. Xem Struct và HashMap.

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 = 3
42 m
y của điểm cuối = 4
▶ Tự chạy thử

Hã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.

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.

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