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

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

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)));
}

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ưng Circle chứa một f64, nên mẫu đúng phải có ngoặc tròn: Circle(_) hoặc Circle(..).
  • “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ánh match
  • let bindings cannot shadow ...: trong let hoặc if let
  • function 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.

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 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.14
hình vuông: 4.00
▶ Tự chạy thử

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

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 struct

Chươ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 m
chiều cao: 1.8 m
tổng: 5.5 m
▶ Tự chạy thử

Tham 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 static

Chươ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ẫu
static 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ả:

đầy
còn chỗ
đầy
▶ Tự chạy thử

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

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";.

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