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.)
Thông báo lỗi
Phần tiêu đề “Thông báo lỗi”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)));}Ý nghĩa
Phần tiêu đề “Ý nghĩa”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::EmptyhoặcNone - 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.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”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
Phần tiêu đề “Cách sửa”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.14hình chữ nhật: 6.00không có gì: 0.001 hình tròn(..) 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à ...
Cách sửa 2: Bỏ () khỏi biến thể unit
Phần tiêu đề “Cách sửa 2: Bỏ () khỏi biến thể unit”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 độngbị cấm (spam)ActiveNế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 = 4trái = -1, lên = 72.5 mVớ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ả:
đầycòn chỗđã đóngđầycòn chỗMatch guard được giải thích ở Match guard: thêm một if.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”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.
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0533: expected unit struct, unit variant or constant, found struct variant: cùng sai sót cho biến thể có trường được đặt tên; hãy thêm
{ .. }. - E0164: expected tuple struct or tuple variant, found struct variant: ngoặc tròn trên biến thể có trường được đặt tên, hoặc trên hằng số hay hàm liên kết.
- E0023: wrong number of fields in a tuple pattern (tiếng Anh): ngoặc tròn thì đúng, nhưng bạn đưa quá nhiều hoặc quá ít tên.
- E0026: struct or variant does not have a field named x: một tên trường sai bên trong ngoặc nhọn của mẫu.
- E0004: non-exhaustive patterns: sau khi sửa các nhánh,
matchvẫn phải bao quát mọi biến thể. - Tất 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. 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
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0532 — Rust error codes index (tiếng Anh): phần giải thích chính thức của lỗi này.
- Pattern Syntax — The Rust Book (tiếng Anh): phân rã struct và enum, bỏ qua giá trị bằng
_và.., và match guard. - Destructuring enums — Rust by Example (tiếng Anh): so khớp biến thể unit và tuple đặt cạnh nhau.
- Destructuring structs — Rust by Example (tiếng Anh): ngoặc nhọn cho trường có tên, ngoặc tròn cho tuple struct.
- Patterns — The Rust Reference (tiếng Anh): các quy tắc chính xác cho mẫu đường dẫn, mẫu tuple struct và mẫu struct.