Match trong Rust
match so sánh một giá trị với một danh sách các mẫu (pattern) và chạy đoạn code của mẫu nào khớp. Nếu bạn từng dùng câu lệnh switch, hãy coi nó như một phiên bản mạnh hơn của switch — còn nếu chưa từng dùng, thì đây vẫn là một trong những công cụ hữu ích nhất nên học sớm.
match cơ bản
Phần tiêu đề “match cơ bản”fn main() { let day = 3;
match day { 1 => println!("Thứ Hai"), 2 => println!("Thứ Ba"), 3 => println!("Thứ Tư"), 4 => println!("Thứ Năm"), 5 => println!("Thứ Sáu"), _ => println!("Cuối tuần"), }}Kết quả:
Thứ TưDấu _ là mẫu bắt mọi thứ (catch-all) — nó khớp với bất cứ thứ gì chưa được liệt kê phía trên. match yêu cầu mọi giá trị có thể có đều phải được xử lý, nên _ thường có mặt để lo “mọi trường hợp còn lại”.
So khớp nhiều giá trị cùng lúc
Phần tiêu đề “So khớp nhiều giá trị cùng lúc”Bạn có thể gom nhiều giá trị vào một nhánh bằng |:
fn main() { let day = 6;
match day { 1 | 2 | 3 | 4 | 5 => println!("Ngày trong tuần"), 6 | 7 => println!("Cuối tuần"), _ => println!("Không phải ngày hợp lệ"), }}So khớp một khoảng
Phần tiêu đề “So khớp một khoảng”Với các con số, bạn có thể so khớp cả một khoảng bằng ..=:
fn main() { let score = 85;
match score { 90..=100 => println!("A"), 75..=89 => println!("B"), 60..=74 => println!("C"), _ => println!("F"), }}match như một biểu thức
Phần tiêu đề “match như một biểu thức”Giống như if, match có thể tạo ra một giá trị:
fn main() { let day = 6;
let kind = match day { 1 | 2 | 3 | 4 | 5 => "ngày thường", 6 | 7 => "cuối tuần", _ => "không rõ", };
println!("{kind}");}So khớp văn bản và ký tự
Phần tiêu đề “So khớp văn bản và ký tự”match không chỉ dành cho con số. Nó cũng dùng được với string slice (&str) và với ký tự, và các khoảng ký tự như 'a'..='z' hoạt động y như khoảng số:
fn main() { let command = "stop"; match command { "start" => println!("Đang khởi động..."), "stop" | "quit" => println!("Đang dừng."), _ => println!("Lệnh không xác định: {command}"), }
let letter = 'k'; let kind = match letter { 'a' | 'e' | 'i' | 'o' | 'u' => "nguyên âm", 'a'..='z' => "phụ âm", '0'..='9' => "chữ số", _ => "thứ gì đó khác", }; println!("'{letter}' là {kind}");
let name = String::from("Ferris"); match name.as_str() { "Ferris" => println!("Chào chú cua!"), _ => println!("Chào {name}!"), }}Kết quả:
Đang dừng.'k' là phụ âmChào chú cua!Có hai điều đáng để ý ở đây:
- Thứ tự rất quan trọng.
'a'cũng nằm trong'a'..='z', nhưng nhánh nguyên âm đứng trước, nên nguyên âm không bao giờ tới được nhánh phụ âm. Rust luôn chọn nhánh đầu tiên khớp. - Một
Stringcần.as_str(). Văn bản viết trong dấu nháy, như"Ferris", là một&str. Nếu bạnmatchtrực tiếp mộtStringvới nó, Rust dừng lại với lỗierror[E0308]: mismatched types ... expected String, found &str(kiểu không khớp: cầnStringnhưng gặp&str). Gọi.as_str()cho bạn một góc nhìn&strcủaStringđể các mẫu so sánh được. (Xem bài Chuỗi để hiểu sự khác nhau giữa hai kiểu này.)
So khớp phân biệt chữ hoa chữ thường: "Stop" sẽ không khớp với "stop". Nếu văn bản đến từ người dùng, một mẹo phổ biến là so khớp trên input.to_lowercase().as_str().
match hay if/else
Phần tiêu đề “match hay if/else”Với những phép kiểm tra đúng/sai đơn giản thì dùng cái nào cũng được, nhưng match bắt đầu chiếm ưu thế khi bạn có nhiều hơn hai ba khả năng — nó dễ đọc hơn một chuỗi else if dài, và trình biên dịch kiểm tra giúp bạn đã xử lý hết mọi trường hợp chưa. if/else vẫn là công cụ phù hợp cho một điều kiện có/không duy nhất.
Luyện tập
Phần tiêu đề “Luyện tập”1. Số ngày trong tháng
Phần tiêu đề “1. Số ngày trong tháng”Lưu một số month (2). Dùng match như một biểu thức để tính tháng đó có bao nhiêu ngày (bỏ qua năm nhuận), rồi in ra Tháng 2 có 28 ngày. Gợi ý: các tháng 1, 3, 5, 7, 8, 10 và 12 có 31 ngày; gom chúng vào một nhánh bằng |.
Xem lời giải
fn main() { let month = 2;
let days = match month { 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, 4 | 6 | 9 | 11 => 30, 2 => 28, _ => 0, // không phải tháng có thật };
println!("Tháng {month} có {days} ngày");}Kết quả:
Tháng 2 có 28 ngày2. Mã trạng thái HTTP
Phần tiêu đề “2. Mã trạng thái HTTP”Web server trả lời bằng một con số. Hãy biến một code thành ý nghĩa ngắn gọn: 200 là "OK", 404 là "Not Found", các mã 400–499 khác là "Lỗi phía client", 500–599 là "Lỗi phía server", và mọi thứ còn lại là "Không rõ". Thử với 503. Gợi ý: đặt nhánh 404 cụ thể phía trên khoảng 400..=499, nếu không nó sẽ không bao giờ được chạy tới.
Xem lời giải
fn main() { let code = 503;
let meaning = match code { 200 => "OK", 404 => "Not Found", // phải đứng trước khoảng 400..=499 400..=499 => "Lỗi phía client", 500..=599 => "Lỗi phía server", _ => "Không rõ", };
println!("{code}: {meaning}");}Kết quả:
503: Lỗi phía serverTóm tắt
Phần tiêu đề “Tóm tắt”matchso sánh một giá trị với các mẫu và chạy nhánh khớp.- Mọi giá trị có thể có đều phải được xử lý — dùng
_làm mẫu bắt mọi thứ. - Gom nhiều giá trị bằng
|, hoặc so khớp một khoảng bằng..=. - Bạn cũng có thể so khớp văn bản
&strvà giá trịchar; dùng.as_str()để so khớp mộtString. match, giốngif, có thể được dùng như một biểu thức để tạo ra giá trị.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Mẫu _ làm gì trong một biểu thức match?
2. Chuyện gì xảy ra nếu một biểu thức match không xử lý hết mọi giá trị có thể có và không có nhánh _?
3. Làm sao để so khớp nhiều giá trị trong một nhánh, như 1, 2 hoặc 3?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- The match Control Flow Construct — The Rust Book (tiếng Anh): chương chính thức, có cả cách dùng
matchvới enum vàOption. - match — Rust by Example (tiếng Anh): một ví dụ gọn gàng chạy được với giá trị,
|, khoảng và_. - Range patterns — The Rust Reference (tiếng Anh): chính xác những kiểu và dạng khoảng nào bạn dùng được trong một mẫu.
- Match expressions — The Rust Reference (tiếng Anh): quy tắc chính xác cho các nhánh, guard và cách một
matchtạo ra giá trị.