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

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

E0532 nghĩa là: hình dạng của mẫu (pattern) không khớp với hình dạng của thứ bạn ghi tên. Trường hợp kinh điển là một nhánh match như Shape::Circle => cho một biến thể có chứa dữ liệu, Circle(f64). Rust muốn có cặp ngoặc tròn: Shape::Circle(_). Sai sót ngược lại cũng nhận cùng mã lỗi, với cách diễn đạt kia là “expected tuple struct or tuple variant”: thêm ngoặc tròn sau một thứ không có dữ liệu nào trong ngoặc, như Status::Active(). Cả hai đều sửa trong một dòng. (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[E0532]: expected unit struct, unit variant or constant, found tuple variant `Shape::Circle`
--> src/main.rs:8:9
|
2 | Circle(f64),
| ----------- `Shape::Circle` defined here
...
8 | Shape::Circle => "circle", // ❌ `Circle` holds a value
| ^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `Shape::Circle(_)`

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 “tuple variant” (biến thể tuple). Đâ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(f64),
Square(f64),
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle => "circle", // ❌ `Circle` holds a value
Shape::Square(_) => "square",
}
}
fn main() {
println!("{}", name(&Shape::Circle(1.0)));
}

Dấu ^^^ chỉ vào Shape::Circle ở dòng 8, còn dấu --- ở dòng 2 cho thấy nó được định nghĩa thế nào: Circle(f64), một biến thể tuple (tuple variant) mang theo một f64. Một mẫu chỉ gồm một cái tên, không có gì phía sau, phải là một trong ba thứ:

  • 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 LIMIT: u32 = 100;

Shape::Circle không thuộc loại nào trong số đó. Dòng help đưa ra cách sửa ngay: Shape::Circle(_), “một hình tròn, giá trị bao nhiêu cũng được”.

Dòng tiêu đề E0532 còn lại là hình ảnh phản chiếu: expected tuple struct or tuple variant, found unit variant `Status::Active` (hoặc found struct, hoặc found constant). Ở đó, bạn đã viết ngoặc tròn sau một cái tên, mà thứ bạn ghi tên lại không nhận giá trị nào trong ngoặc tròn.

Giống như một tờ khai hỏi “Thú cưng: chó (tên: ____)”. Chỉ ghi “chó” thì ô tên bị bỏ trống, còn ghi “cá (tên: Bống)” ở chỗ tờ khai chỉ có chữ “cá” mà không có ô nào thì bạn đang thêm một ô không hề tồn tại.

Mỗi struct và biến thể enum có một trong ba hình dạng, và bạn viết nó theo cùng một cách khi tạo lẫn khi so khớp (xem Enum có thể chứa dữ liệu):

Loại Cách tạo Cách so khớp
Unit: Empty Shape::Empty Shape::Empty
Tuple: Circle(f64) Shape::Circle(1.0) Shape::Circle(r) hoặc Shape::Circle(..)
Struct: Rect { w: f64, h: f64 } Shape::Rect { w: 2.0, h: 3.0 } Shape::Rect { w, h } hoặc Shape::Rect { .. }

Vì hình dạng được giữ chặt chẽ, một mẫu luôn trông giống giá trị nó so khớp, và bạn đọc một match là thấy mỗi nhánh có dữ liệu gì. Điều này cũng giúp ích khi bạn thay đổi một kiểu: nếu sau này Empty có thêm một giá trị và trở thành Empty(u32), mọi nhánh Shape::Empty cũ đều bị đánh dấu, thay vì âm thầm khớp với một thứ mới.

Cách sửa 1: Thêm (_), (..) hoặc tên cho các giá trị

Phần tiêu đề “Cách sửa 1: Thêm (_), (..) hoặc tên cho các giá trị”

Chọn cách này khi thông báo nói found tuple variant hoặc found tuple struct, dạng E0532 phổ biến nhất. Dùng _ cho mỗi giá trị bạn không cần, .. để bỏ qua tất cả cùng lúc, hoặc một cái tên khi bạn muốn dùng giá trị đó:

enum Shape {
Circle(f64),
Rect(f64, f64),
Empty,
}
fn name(shape: &Shape) -> &'static str {
match shape {
Shape::Circle(_) => "hình tròn", // ✅ một trường, bỏ qua
Shape::Rect(..) => "hình chữ nhật", // ✅ bao nhiêu trường cũng được, bỏ qua hết
Shape::Empty => "không có gì", // biến thể unit: không có ngoặc tròn
}
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(r) => 3.14 * r * r, // ✅ gắn tên cho trường vì cần dùng nó
Shape::Rect(w, h) => w * h,
Shape::Empty => 0.0,
}
}
fn main() {
let shapes = [Shape::Circle(1.0), Shape::Rect(2.0, 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 chữ nhật: 6.00
không có gì: 0.00
1 hình tròn
▶ Tự chạy thử

(..) tiện cho các biến thể có nhiều giá trị, và vẫn dùng được nếu sau này bạn thêm một giá trị nữa. Xem Bỏ qua một phần với _ và ...

Chọn cách này khi thông báo nói found unit variant hoặc found unit struct. Biến thể không chứa dữ liệu, nên chẳng có gì để đặt trong ngoặc tròn:

error[E0532]: expected tuple struct or tuple variant, found unit variant `Status::Active`
--> src/main.rs:8:9
|
2 | Active,
| ------ `Status::Active` defined here
...
8 | Status::Active() => "active", // ❌ `Active` holds no data
| ^^^^^^^^^^^^^^^^ help: use this syntax instead: `Status::Active`

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

enum Status {
Active,
Banned(String),
}
fn label(s: &Status) -> &'static str {
match s {
Status::Active() => "active", // ❌ `Active` holds no data
Status::Banned(_) => "banned",
}
}
fn main() {
println!("{}", label(&Status::Active));
}

Chỉ viết cái tên, cả trong mẫu lẫn ở chỗ tạo giá trị:

#[derive(Debug)]
enum Status {
Active,
Banned(String),
}
fn label(s: &Status) -> String {
match s {
Status::Active => String::from("đang hoạt động"), // ✅ biến thể unit: không có ()
Status::Banned(reason) => format!("bị cấm ({})", reason),
}
}
fn main() {
println!("{}", label(&Status::Active));
println!("{}", label(&Status::Banned(String::from("spam"))));
let s = Status::Active; // ✅ khi tạo giá trị cũng không có ()
println!("{:?}", s);
}

Kết quả:

đang hoạt động
bị cấm (spam)
Active
▶ Tự chạy thử

Nếu bạn muốn biến thể mang theo dữ liệu, hãy đổi định nghĩa, ví dụ Active(u64) cho mốc thời gian “từ khi nào”, và khi đó Status::Active(_) mới là mẫu đúng.

Cách sửa 3: Dùng ngoặc nhọn cho struct có trường được đặt tên

Phần tiêu đề “Cách sửa 3: Dùng ngoặc nhọn cho struct có trường được đặt tên”

Chọn cách này khi thông báo nói found struct. Một struct có trường được đặt tên, struct Point { x: i32, y: i32 }, được phân rã bằng ngoặc nhọn và tên trường, không phải bằng ngoặc tròn:

error[E0532]: expected tuple struct or tuple variant, found struct `Point`
--> src/main.rs:8:9
|
1 | / struct Point {
2 | | x: i32,
3 | | y: i32,
4 | | }
| |_- `Point` defined here
...
8 | let Point(x, y) = p; // ❌ `Point` has named fields
| ^^^^^^^^^^^ help: use struct pattern syntax instead: `Point { x, y }`

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

struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 3, y: 4 };
let Point(x, y) = p; // ❌ `Point` has named fields
println!("{} {}", x, y);
}

Gợi ý Point { x, y } của rustc hoàn toàn đúng. Dùng field: name khi bạn muốn tên biến khác:

struct Point {
x: i32,
y: i32,
}
struct Meters(f64); // một tuple struct: ở đây ngoặc tròn là đúng
fn main() {
let p = Point { x: 3, y: 4 };
let Point { x, y } = p; // ✅ ngoặc nhọn và tên trường
println!("x = {}, y = {}", x, y);
let q = Point { x: -1, y: 7 };
let Point { x: left, y: up } = q; // ✅ đổi tên khi phân rã
println!("trái = {}, lên = {}", left, up);
let Meters(m) = Meters(2.5); // ✅ tuple struct, mẫu tuple
println!("{} m", m);
}

Kết quả:

x = 3, y = 4
trái = -1, lên = 7
2.5 m
▶ Tự chạy thử

Với một biến thể enum có trường được đặt tên, Shape::Rect(w, h) cho Rect { w: f64, h: f64 }, cùng sai sót đó cho ra mã lỗi khác, E0164. Cách sửa vẫn vậy: dùng ngoặc nhọn.

Cách sửa 4: Dùng hằng số hoặc match guard thay cho lời gọi hàm

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

Chọn cách này khi thông báo nói expected a pattern, found a function call (cần một mẫu, nhưng gặp một lời gọi hàm). Một mẫu phải được biết từ lúc biên dịch, mà lời gọi hàm thì không:

error[E0532]: expected a pattern, found a function call
--> src/main.rs:7:14
|
7 | Some(limit()) => "full", // ❌ a function call can't be a pattern
| ^^^^^ not a tuple struct or tuple variant
|
= note: function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>

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

fn limit() -> u32 {
100
}
fn status(users: Option<u32>) -> &'static str {
match users {
Some(limit()) => "full", // ❌ a function call can't be a pattern
Some(_) => "open",
None => "closed",
}
}
fn main() {
println!("{}", status(Some(100)));
}

Nếu giá trị không bao giờ thay đổi, hãy biến nó thành một const; hằng số dùng được bên trong mẫu. Nếu giá trị phải được tính ra, hãy gắn giá trị vào một cái tên và so sánh trong một match guard:

const LIMIT: u32 = 100; // hằng số dùng được bên trong mẫu
fn limit_for(premium: bool) -> u32 {
if premium { 500 } else { 100 }
}
fn status(users: Option<u32>) -> &'static str {
match users {
Some(LIMIT) => "đầy", // ✅ một hằng số
Some(_) => "còn chỗ",
None => "đã đóng",
}
}
fn status_for(users: Option<u32>, premium: bool) -> &'static str {
match users {
Some(n) if n >= limit_for(premium) => "đầy", // ✅ gọi hàm trong guard
Some(_) => "còn chỗ",
None => "đã đóng",
}
}
fn main() {
println!("{}", status(Some(100)));
println!("{}", status(Some(42)));
println!("{}", status(None));
println!("{}", status_for(Some(100), false));
println!("{}", status_for(Some(100), true));
}

Kết quả:

đầy
còn chỗ
đã đóng
đầy
còn chỗ
▶ 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 giá trị. Event::Logout từng là biến thể unit, và bạn đổi nó thành Logout(u64). Giờ mọi nhánh Event::Logout => đều báo E0532. Cách sửa: Event::Logout(_) =>, hoặc Event::Logout(at) => nếu bạn cần giá trị.

Chỉ kiểm tra loại của một giá trị. Nếu bạn đến từ Java hay TypeScript, bạn có thể viết if let Shape::Circle = shape để hỏi “nó có phải hình tròn không?”. Hãy viết if let Shape::Circle(_) = shape, hoặc matches!(shape, Shape::Circle(_)) để có nhanh một bool.

Thiếu tiền tố Shape:: thì là E0530. Nếu bạn viết một cái tên trơn, như Ok => hoặc Circle => sau use Shape::*;, rustc hiểu nó là một biến mới. Nó báo E0530, match bindings cannot shadow tuple variants, kèm help try specify the pattern arguments: `Ok(..)` . Cách sửa vẫn vậy: thêm ngoặc tròn.

Kiểm tra nhanh

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

2. rustc báo “expected tuple struct or tuple variant, found unit variant Status::Active”. Cách sửa là gì?

3. struct Point { x: i32, y: i32 }: bạn phân rã nó bằng let thế nào?

Điểm: 0 / 3