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

Lỗi Rust E0533: expected unit struct, unit variant or constant, found struct variant

E0533 nghĩa là: bạn viết một cái tên như Shape::Circle đứng một mình, nhưng ở chỗ đó cái tên ấy cần nhiều hơn chỉ cái tên. Thường gặp nhất là một biến thể enum có trường được đặt tên (Circle { radius: f64 }) bị viết thiếu cặp { }, trong một nhánh match hoặc khi tạo giá trị. Ít gặp hơn là một hàm hoặc phương thức bị viết ở chỗ chỉ được đặt một giá trị cố định. Cả hai đều sửa nhanh, một khi bạn hiểu rustc đang đòi gì. (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[E0533]: expected unit struct, unit variant or constant, found struct variant `Shape::Circle`
--> src/main.rs:8:9
|
8 | Shape::Circle => "circle", // ❌ `Circle` has a field
| ^^^^^^^^^^^^^ not a unit struct, unit variant or constant
|
help: the struct variant's field is being ignored
|
8 | Shape::Circle { radius: _ } => "circle", // ❌ `Circle` has a field
| +++++++++++++

Thông báo của trình biên dịch luôn bằng tiếng Anh: nó nói rằng ở đây cần “unit struct, unit variant hoặc hằng số”, nhưng lại gặp một “struct variant” (biến thể dạng struct). Đây là chương trình gây ra lỗi đó (chuỗi và chú thích được giữ bằng tiếng Anh vì chúng xuất hiện nguyên văn trong thông báo lỗi):

enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle => "circle", // ❌ `Circle` has a field
Shape::Square { .. } => "square",
}
}
fn main() {
println!("{}", name(&Shape::Square { side: 2.0 }));
}

Dấu ^^^ chỉ vào Shape::Circle, và dòng tiêu đề giải thích thứ được phép đặt ở đó: một mẫu (pattern) chỉ gồm một cái tên phải là một trong ba thứ sau:

  • một unit struct, như struct Empty;
  • một unit variant (biến thể không chứa dữ liệu), như Shape::Empty hoặc None
  • một hằng số (constant), như const MAX: u32 = 100;

Shape::Circle không thuộc loại nào trong số đó. Nó là một biến thể dạng struct (struct variant): nó mang theo một radius. Một Shape::Circle trơn sẽ khớp với “một hình tròn” nhưng không nói gì về trường của nó, mà Rust thì muốn hình dạng của mẫu khớp với hình dạng của biến thể. Dòng help: cho thấy cách sửa: thêm cặp ngoặc nhọn.

Dạng thứ hai của E0533 có nội dung expected value, found struct variant (cần một giá trị, nhưng gặp biến thể dạng struct). Cùng một sự nhầm lẫn, nhưng xảy ra khi tạo giá trị, thường được viết bằng ngoặc tròn như Shape::Circle(2.0).

Giống như một tờ phiếu gửi hàng có ô “Kích cỡ: S, M hoặc L”, và bạn ghi “Tùy chỉnh”. Tùy chỉnh là một lựa chọn có thật, nhưng phải điền số đo ngay bên cạnh.

Biến thể enum có ba hình dạng, và mỗi hình dạng được viết theo cách riêng, cả khi tạo lẫn khi so khớp (xem Enum có thể chứa dữ liệu):

Biến thể Cách tạo Cách so khớp
Unit: Empty Shape::Empty Shape::Empty
Tuple: Point(f64, f64) Shape::Point(1.0, 2.0) Shape::Point(x, y) hoặc Shape::Point(..)
Struct: Circle { radius: f64 } Shape::Circle { radius: 2.0 } Shape::Circle { radius } hoặc Shape::Circle { .. }

Giữ hình dạng chặt chẽ nghĩa là một mẫu luôn trông giống thứ nó so khớp, nên bạn đọc một match là biết chính xác mỗi nhánh có dữ liệu gì. Nó cũng có nghĩa là khi một biến thể đổi hình dạng, ví dụ khi Empty có thêm một trường, mọi chỗ còn dùng hình dạng cũ đều bị đánh dấu, thay vì âm thầm làm sai.

Cách sửa 1: Thêm { .. } hoặc các trường vào mẫu

Phần tiêu đề “Cách sửa 1: Thêm { .. } hoặc các trường vào mẫu”

Chọn cách này khi lỗi chỉ vào một nhánh match, if let hoặc matches! và nói found struct variant. Viết { .. } nếu bạn không cần các trường, hoặc ghi tên chúng nếu cần. Gợi ý { radius: _ } của rustc cũng dùng được, và { .. } là cách nói ngắn hơn cho cùng một ý.

enum Shape {
Circle { radius: f64 },
Square { side: f64 },
Empty,
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle { .. } => "hình tròn", // ✅ “một hình tròn, bán kính bao nhiêu cũng được”
Shape::Square { .. } => "hình vuông",
Shape::Empty => "không có gì", // biến thể unit: không cần ngoặc nhọn
}
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius } => 3.14 * radius * radius, // ✅ ghi tên trường vì cần dùng chúng
Shape::Square { side } => side * side,
Shape::Empty => 0.0,
}
}
fn main() {
let shapes = [Shape::Circle { radius: 1.0 }, Shape::Square { side: 3.0 }, Shape::Empty];
for shape in &shapes {
println!("{}: {:.2}", name(shape), area(shape));
}
let circles = shapes.iter().filter(|s| matches!(s, Shape::Circle { .. })).count();
println!("{} hình tròn", circles);
}

Kết quả:

hình tròn: 3.14
hình vuông: 9.00
không có gì: 0.00
1 hình tròn
▶ Tự chạy thử

{ .. } dùng được cho mọi biến thể dạng struct, dù nó có bao nhiêu trường. Với biến thể tuple, cùng ý tưởng đó được viết là (..). Xem Bỏ qua một phần với _ và ...

Cách sửa 2: Tạo giá trị bằng ngoặc nhọn và tên trường

Phần tiêu đề “Cách sửa 2: Tạo giá trị bằng ngoặc nhọn và tên trường”

Chọn cách này khi thông báo nói expected value, found struct variant. Bạn đang tạo một biến thể dạng struct như thể nó là biến thể tuple (hoặc không có trường nào):

error[E0533]: expected value, found struct variant `Shape::Circle`
--> src/main.rs:8:17
|
8 | let shape = Shape::Circle(2.0); // ❌ `Circle` has a named field
| ^^^^^^^^^^^^^ not a value
|
help: you might have meant to create a new value of the struct
|
8 - let shape = Shape::Circle(2.0); // ❌ `Circle` has a named field
8 + let shape = Shape::Circle { radius: /* value */ }; // ❌ `Circle` has a named field
|

Chương trình đằng sau lỗi này:

#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
fn main() {
let shape = Shape::Circle(2.0); // ❌ `Circle` has a named field
println!("{:?}", shape);
}

/* value */ trong gợi ý là chỗ để bạn điền giá trị. Các trường của một biến thể dạng struct được gán theo tên, bên trong ngoặc nhọn:

#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Square { side: f64 },
}
impl Shape {
// một hàm tạo ngắn gọn, nếu bạn muốn viết `Shape::circle(2.0)`
fn circle(radius: f64) -> Shape {
Shape::Circle { radius }
}
}
fn main() {
let a = Shape::Circle { radius: 2.0 }; // ✅ ngoặc nhọn và tên trường
let b = Shape::Square { side: 1.5 };
let c = Shape::circle(0.5);
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}

Kết quả:

Circle { radius: 2.0 }
Square { side: 1.5 }
Circle { radius: 0.5 }
▶ Tự chạy thử

Nếu bạn thích viết Shape::Circle(2.0) hơn, hãy đổi định nghĩa thành biến thể tuple: Circle(f64). Đó là lựa chọn tốt khi chỉ có một trường hiển nhiên; còn khi có nhiều trường, trường có tên sẽ rõ ràng hơn.

Cách sửa 3: Dùng hằng số hoặc match guard thay cho hàm

Phần tiêu đề “Cách sửa 3: Dùng hằng số hoặc match guard thay cho hàm”

Chọn cách này khi thông báo nói found associated function (gặp hàm liên kết). Nó cũng xuất hiện với phương thức, như String::len. Một mẫu phải là giá trị được biết từ lúc biên dịch. Lời gọi hàm thì không, nên tên một hàm không thể dùng làm mẫu:

error[E0533]: expected unit struct, unit variant or constant, found associated function `Limits::max_users`
--> src/main.rs:12:9
|
12 | Limits::max_users => "full", // ❌ a function can't be a pattern
| ^^^^^^^^^^^^^^^^^ not a unit struct, unit variant or constant

Chương trình đằng sau lỗi này:

struct Limits;
impl Limits {
fn max_users() -> u32 {
100
}
}
fn status(users: u32) -> &'static str {
match users {
0 => "empty",
Limits::max_users => "full", // ❌ a function can't be a pattern
_ => "open",
}
}
fn main() {
println!("{}", status(100));
}

Nếu giá trị không bao giờ thay đổi, hãy biến nó thành một const. Hằng số có thể làm mẫu. Nếu giá trị phải được tính ra, hãy giữ hàm và so sánh trong một match guard với if:

struct Limits;
impl Limits {
const MAX_USERS: u32 = 100; // hằng số có thể dùng làm mẫu
fn max_rooms(premium: bool) -> u32 {
if premium { 20 } else { 5 }
}
}
fn status(users: u32) -> &'static str {
match users {
0 => "trống",
Limits::MAX_USERS => "đầy", // ✅ một hằng số
_ => "còn chỗ",
}
}
fn rooms_status(rooms: u32, premium: bool) -> &'static str {
match rooms {
n if n >= Limits::max_rooms(premium) => "hết phòng rồi", // ✅ gọi hàm trong guard
_ => "bạn có thể thêm phòng",
}
}
fn main() {
println!("{}", status(0));
println!("{}", status(100));
println!("{}", status(42));
println!("{}", rooms_status(5, false));
println!("{}", rooms_status(5, true));
}

Kết quả:

trống
đầy
còn chỗ
hết phòng rồi
bạn có thể thêm phòng
▶ Tự chạy thử

Match guard được giải thích ở Match guard: thêm một if.

Sau khi một biến thể unit có thêm trường. Status::Done từng là biến thể unit, và bạn đổi nó thành Done { at: u64 }. Giờ mọi nhánh Status::Done => và mọi dòng let s = Status::Done; đều báo E0533. Cách sửa: Status::Done { .. } => trong mẫu, và Status::Done { at: 0 } (với giá trị thật) ở chỗ tạo nó.

Khi bạn đến từ ngôn ngữ khác. Trong Java hay TypeScript, bạn có thể viết case Circle: để chỉ kiểm tra loại của một giá trị. Trong Rust, hãy viết Shape::Circle { .. } =>, hoặc matches!(shape, Shape::Circle { .. }) để có nhanh một bool.

Phiên bản biến thể tuple. Với một biến thể tuple như Circle(f64), một nhánh Shape::Circle => trơn sẽ cho ra mã lỗi khác, E0532, với cùng dòng tiêu đề “expected unit struct, unit variant or constant, found tuple variant Shape::Circle”. Cách sửa là Shape::Circle(_) hoặc Shape::Circle(..). Còn dùng ngoặc tròn cho biến thể dạng struct trong mẫu, như Shape::Circle(r) => với Circle { radius: f64 }, sẽ cho ra E0164, “expected tuple struct or tuple variant, found struct variant”. Cách sửa: Shape::Circle { radius: r } =>.

Kiểm tra nhanh

1. Với enum Shape { Circle { radius: f64 } }, nhánh match nào biên dịch được?

2. let s = Shape::Circle(2.0); báo “expected value, found struct variant”. Cách sửa là gì?

3. Thứ nào dưới đây có thể đứng một mình làm mẫu trong match?

Điểm: 0 / 3