Lỗi Rust E0530: match bindings cannot shadow tuple variants (tên đã bị chiếm)
E0530 nghĩa là: một cái tên trong mẫu (pattern) của bạn đã có chủ. Bạn viết một cái tên trơn, như Circle, ở chỗ mà bình thường Rust sẽ tạo một biến mới. Nhưng Circle đã mang một nghĩa khác trong phạm vi này: một biến thể tuple, một tuple struct, hoặc một static. Rust không chịu lặng lẽ che nó đi bằng một biến mới, vì điều đó gần như không bao giờ là ý bạn. Trường hợp kinh điển là một nhánh match Circle => sau use Shape::*;, và cách sửa thường chỉ là hai ký tự: Circle(..). (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[E0530]: match bindings cannot shadow tuple variants --> src/main.rs:10:9 | 6 | use Shape::*; | -------- the tuple variant `Circle` is imported here...10 | Circle => "circle", // ❌ `Circle` holds a value | ^^^^^^ | | | cannot be named the same as a tuple variant | help: try specify the pattern arguments: `Circle(..)`Thông báo của trình biên dịch luôn bằng tiếng Anh: “match bindings cannot shadow tuple variants” nghĩa là “biến trong nhánh match không được che khuất 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),}
use Shape::*;
fn name(shape: &Shape) -> &'static str { match shape { Circle => "circle", // ❌ `Circle` holds a value Square(_) => "square", }}
fn main() { println!("{}", name(&Circle(1.0)));}Ý nghĩa
Phần tiêu đề “Ý nghĩa”Dấu ^^^ chỉ vào Circle ở dòng 10, còn dấu --- ở dòng 6 cho thấy cái tên đó đến từ đâu: use Shape::*; đã đưa biến thể Shape::Circle vào phạm vi.
Trong một mẫu, một cái tên trơn như x hoặc other sẽ tạo một biến mới khớp với mọi thứ. Vì vậy Circle => có thể mang hai nghĩa:
- “khớp với biến thể
Circle”, nhưngCirclechứa mộtf64, nên mẫu đúng phải có ngoặc tròn:Circle(_)hoặcCircle(..). - “khớp với mọi thứ và gọi nó là
Circle”, một biến mới sẽ che khuất biến thể.
Rust không đoán. Dòng tiêu đề cho bạn biết bạn đã đụng phải loại tên nào, và từ đầu tiên cho biết chuyện xảy ra ở đâu:
match bindings cannot shadow ...: trong một nhánhmatchlet bindings cannot shadow ...: tronglethoặcif letfunction parameters cannot shadow ...: trong danh sách tham số của hàm
và câu kết thúc bằng tuple variants, tuple structs hoặc statics.
Giống như đặt tên cho chú chó là “Bác sĩ”. Mỗi lần ai đó gọi “Bác sĩ ơi!” trong phòng chờ, chẳng ai biết người ta đang gọi ai.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”Nếu không có quy tắc này, Circle => sẽ là một biến mới tên Circle. Nó sẽ khớp với mọi giá trị, và mọi nhánh phía sau đều thành code chết. Chương trình của bạn vẫn biên dịch, rồi in “circle” cả cho hình vuông. Đó là một bug khó chịu, núp sau đoạn code trông có vẻ đúng.
Vì vậy Rust báo lỗi mỗi khi một biến mới định lấy tên của một biến thể tuple, một tuple struct hoặc một static. Bạn hoặc nói rõ ý mình bằng ngoặc tròn, hoặc chọn tên khác. Biến thể unit như None và hằng số (const) thì khác: tự chúng đã là mẫu hợp lệ, nên Rust hiểu chúng là “khớp với giá trị này”. Xem So khớp mẫu và Enum.
Cách sửa
Phần tiêu đề “Cách sửa”Cách sửa 1: Thêm (..) hoặc (_) để khớp với biến thể
Phần tiêu đề “Cách sửa 1: Thêm (..) hoặc (_) để khớp với biến thể”Chọn cách này khi thông báo nói tuple variants hoặc tuple structs, trường hợp phổ biến nhất. Ý bạn là biến thể, vậy hãy cho nó cặp ngoặc tròn, như help của rustc gợi ý. Viết đầy đủ đường dẫn, Shape::Circle(_), còn rõ ràng hơn nữa:
enum Shape { Circle(f64), Square(f64),}
use Shape::*;
fn name(shape: &Shape) -> &'static str { match shape { Circle(..) => "hình tròn", // ✅ biến thể, bên trong là gì cũng được Square(_) => "hình vuông", }}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(r) => 3.14 * r * r, // ✅ đường dẫn đầy đủ, và dùng giá trị Shape::Square(side) => side * side, }}
fn main() { let shapes = [Circle(1.0), Square(2.0)]; for shape in &shapes { println!("{}: {:.2}", name(shape), area(shape)); }}Kết quả:
hình tròn: 3.14hình vuông: 4.00.. nghĩa là “bên trong là gì cũng được”, và vẫn dùng được nếu sau này biến thể có thêm giá trị. Xem Bỏ qua một phần với _ và ...
Cách sửa 2: Đổi tên biến
Phần tiêu đề “Cách sửa 2: Đổi tên biến”Chọn cách này khi bạn thật sự muốn một biến mới, chỉ là tên của nó tình cờ trùng với một kiểu. Điều này hay xảy ra với let và tham số hàm, khi bạn đặt tên biến theo kiểu của nó:
error[E0530]: let bindings cannot shadow tuple structs --> src/main.rs:4:9 |1 | struct Meters(f64); | ------------------- the tuple struct `Meters` is defined here...4 | let Meters = 5.0; // ❌ `Meters` is a tuple struct | ^^^^^^ cannot be named the same as a tuple structChương trình đằng sau lỗi này:
struct Meters(f64);
fn main() { let Meters = 5.0; // ❌ `Meters` is a tuple struct println!("{}", Meters);}Quy ước đặt tên của Rust giải quyết chuyện này giúp bạn: kiểu và biến thể viết CamelCase, biến viết snake_case. Dùng tên viết thường là hết xung đột:
struct Meters(f64);
fn total(meters: &[Meters]) -> f64 { meters.iter().map(|m| m.0).sum()}
fn main() { let meters = 5.0; // ✅ tên biến viết thường println!("{} m", meters);
let Meters(height) = Meters(1.8); // ✅ hoặc phân rã tuple struct println!("chiều cao: {} m", height);
println!("tổng: {} m", total(&[Meters(2.0), Meters(3.5)]));}Kết quả:
5 mchiều cao: 1.8 mtổng: 5.5 mTham số hàm cũng vậy: fn show(Point: i32) cho ra function parameters cannot shadow tuple structs, và fn show(point: i32) sửa được nó. Xem Quy tắc đặt tên.
Cách sửa 3: Dùng const (hoặc match guard) thay cho static
Phần tiêu đề “Cách sửa 3: Dùng const (hoặc match guard) thay cho static”Chọn cách này khi thông báo nói statics. Có lẽ bạn muốn so sánh với một giá trị cố định. Một static không thể dùng làm mẫu, nhưng một const thì có thể:
error[E0530]: match bindings cannot shadow statics --> src/main.rs:5:9 |1 | static LIMIT: u32 = 100; | ------------------------ the static `LIMIT` is defined here...5 | LIMIT => "full", // ❌ a static can't be a pattern | ^^^^^ cannot be named the same as a staticChương trình đằng sau lỗi này:
static LIMIT: u32 = 100;
fn status(users: u32) -> &'static str { match users { LIMIT => "full", // ❌ a static can't be a pattern _ => "open", }}
fn main() { println!("{}", status(100));}Đổi static thành const. Nếu nó buộc phải giữ là static (ví dụ vì code khác lấy địa chỉ của nó), 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; // ✅ const dùng được làm mẫustatic MAX_USERS: u32 = 500; // static vẫn giữ là static
fn status(users: u32) -> &'static str { match users { LIMIT => "đầy", _ => "còn chỗ", }}
fn premium_status(users: u32) -> &'static str { match users { n if n >= MAX_USERS => "đầy", // ✅ so sánh static trong guard _ => "còn chỗ", }}
fn main() { println!("{}", status(100)); println!("{}", status(42)); println!("{}", premium_status(500));}Kết quả:
đầycòn chỗđầyVới một con số đơn giản, bạn hiếm khi cần đến static. const là lựa chọn thông thường; xem Hằng số.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”Ok => hoặc Some => không có ngoặc tròn. Ok, Err và Some là các biến thể tuple trong prelude, nên match result { Ok => ... } gây ra E0530 với help try specify the pattern arguments: `Ok(..)` . Cách sửa: Ok(_) => hoặc Ok(value) =>.
Bên trong if let. if let Some(Some) = x cố đặt tên cho giá trị bên trong là Some và gây ra let bindings cannot shadow tuple variants. Cách sửa: một tên viết thường, if let Some(inner) = x.
Có đường dẫn đầy đủ thì lại là E0532. Shape::Circle => không thể là một biến mới (tên biến không chứa ::), nên rustc báo E0532, expected unit struct, unit variant or constant, found tuple variant. Cách sửa vẫn vậy: thêm (_) hoặc (..).
let với tên của một unit struct là E0308. Với struct Guest;, dòng let Guest = "Ana"; hoàn toàn không tạo biến nào: rustc hiểu Guest là unit struct và báo E0308 (kiểu không khớp), nói rằng `Guest` is interpreted as a unit struct, not a new binding (Guest được hiểu là unit struct, không phải biến mới). Cách sửa: đổi tên biến, let guest = "Ana";.
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0532: expected unit struct, unit variant or constant, found tuple variant: cùng sai sót nhưng viết có đường dẫn, như
Shape::Circle =>. - E0533: expected unit struct, unit variant or constant, found struct variant: một biến thể có trường được đặt tên bị dùng mà thiếu
{ .. }. - E0004: non-exhaustive patterns: sau khi bạn sửa các nhánh,
matchvẫn phải bao quát mọi biến thể. - E0308: mismatched types: lỗi mà một
letmang tên unit struct gây ra. - E0425: cannot find value in this scope: vấn đề ngược lại, một cái tên hoàn toàn không tồn tại.
- 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. Sau use Shape::*;, với Circle(f64) là một biến thể, nhánh match nào biên dịch được?
2. rustc báo “let bindings cannot shadow tuple structs” cho let Meters = 5.0;. Cách sửa đơn giản nhất là gì?
3. static LIMIT: u32 = 100; không dùng được làm mẫu trong match. Thứ gì dùng được?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0530 — Rust error codes index (tiếng Anh): phần giải thích chính thức, với trường hợp thiếu
(_)và trường hợpstaticso vớiconst. - Pattern Syntax — The Rust Book (tiếng Anh): so khớp biến có tên, phân rã enum, và bỏ qua giá trị bằng
_và... - Identifier patterns — The Rust Reference (tiếng Anh): quy tắc chính xác về khi nào một cái tên trong mẫu là biến mới và khi nào nó chỉ tới thứ đã tồn tại.
- Destructuring enums — Rust by Example (tiếng Anh): so khớp biến thể unit và tuple đặt cạnh nhau.
- Static items — The Rust Reference (tiếng Anh):
statickhácconstthế nào (một vị trí cố định trong bộ nhớ, không phải một giá trị được chép vào mỗi chỗ dùng).