If let và while let trong Rust
Bạn đã dùng match để xử lý Option và Result. Tuy vậy, phần lớn thời gian bạn chỉ thực sự quan tâm đến một mẫu và muốn bỏ qua mọi thứ còn lại. Viết cả một match với nhánh _ => {} chỉ để không làm gì thì vẫn chạy, nhưng hơi rườm rà so với nhu cầu. if let là lối tắt.
Vấn đề khi dùng cả một match ở đây
Phần tiêu đề “Vấn đề khi dùng cả một match ở đây”fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("Tuổi là {value}"), _ => {}, // thực ra chúng ta không quan tâm đến trường hợp này }}Nhánh _ => {} đó chẳng làm gì ngoài việc thỏa mãn quy tắc “phải xử lý mọi trường hợp” của match. Nếu tất cả những gì bạn muốn là làm gì đó với Some, thì chừng này code là quá nhiều cho một ý tưởng đơn giản.
if let
Phần tiêu đề “if let”fn main() { let age: Option<i32> = Some(30);
if let Some(value) = age { println!("Tuổi là {value}"); }}Kết quả:
Tuổi là 30Hãy đọc nó là: “nếu age khớp với mẫu Some(value), chạy khối này, với value được gắn vào thứ nằm bên trong.” Nếu age là None, khối đơn giản là bị bỏ qua — không lỗi, không in gì cả.
Thêm else
Phần tiêu đề “Thêm else”if let có thể có else, giống như một if bình thường:
fn main() { let age: Option<i32> = None;
if let Some(value) = age { println!("Tuổi là {value}"); } else { println!("Không có thông tin tuổi"); }}if let với Result
Phần tiêu đề “if let với Result”Nó hoạt động y như vậy với Result, thứ bạn đã gặp ở bài Result:
fn main() { let input = "42";
if let Ok(number) = input.parse::<i32>() { println!("Đã chuyển được: {number}"); } else { println!("Không chuyển được chuỗi đó thành số"); }}Nối tiếp với else if let
Phần tiêu đề “Nối tiếp với else if let”Giống như if có else if, theo sau if let có thể là else if let. Rust lần lượt thử từng mẫu và chạy khối đầu tiên khớp. Cách này tiện khi bạn có vài chỗ để tìm một giá trị, theo thứ tự ưu tiên:
fn main() { let nickname: Option<&str> = None; let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname { println!("Chào {nick}!"); } else if let Some(name) = full_name { println!("Xin chào, {name}."); } else { println!("Xin chào, người lạ."); }}Kết quả:
Xin chào, Ferris Crab.nickname là None, nên khối đầu tiên bị bỏ qua. full_name khớp với Some(name), nên khối đó chạy, và else cuối cùng không bao giờ được chạy tới. Bạn cũng có thể chen vào một điều kiện thông thường, như else if is_admin { ... }, ở bất cứ đâu trong chuỗi.
Mỗi biến chỉ tồn tại bên trong khối của riêng nó: nick không dùng được trong khối thứ hai, và name không dùng được sau khi chuỗi kết thúc. Nếu bạn cần giá trị đã mở ra cho phần còn lại của code, hãy xem let-else ở bài So khớp mẫu.
while let
Phần tiêu đề “while let”while let áp dụng cùng ý tưởng đó cho vòng lặp: cứ lặp chừng nào giá trị còn khớp với một mẫu. Nó đặc biệt tiện để lấy hết phần tử ra khỏi một collection:
fn main() { let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() { println!("{top}"); }}Kết quả:
321.pop() trả về Some(value) khi vẫn còn phần tử, và None khi vector đã rỗng — lúc đó while let tự động dừng vòng lặp. Không cần tự kiểm tra độ dài.
if let hay match — nên dùng cái nào
Phần tiêu đề “if let hay match — nên dùng cái nào”Dùng match khi bạn thực sự cần xử lý nhiều trường hợp theo những cách khác nhau. Dùng if let khi thực sự chỉ có một mẫu bạn quan tâm và mọi thứ khác chỉ cần bỏ qua. Nó không thay thế match — chỉ là cách viết ngắn hơn cho tình huống phổ biến “tôi chỉ quan tâm một nhánh”.
Luyện tập
Phần tiêu đề “Luyện tập”1. Cộng dồn một ngăn xếp
Phần tiêu đề “1. Cộng dồn một ngăn xếp”Bắt đầu với let mut numbers = vec![4, 8, 15]; và một biến cộng dồn total bằng 0. Dùng while let với .pop() để lấy từng số ra từ cuối, cộng mỗi số vào total, rồi in số đó và tổng mới. Gợi ý: total cần có mut, và total += n cộng thêm vào nó.
Xem lời giải
fn main() { let mut numbers = vec![4, 8, 15]; let mut total = 0;
while let Some(n) = numbers.pop() { total += n; println!("lấy ra {n}, tổng bây giờ là {total}"); }}Kết quả:
lấy ra 15, tổng bây giờ là 15lấy ra 8, tổng bây giờ là 23lấy ra 4, tổng bây giờ là 27.pop() lấy từ cuối, đó là lý do 15 được lấy ra đầu tiên.
2. Kiểm tra tuổi từ văn bản
Phần tiêu đề “2. Kiểm tra tuổi từ văn bản”Lưu let input = "17";. Nếu nó chuyển được thành u32, hãy in ra tuổi đó là người lớn (từ 18 trở lên) hay trẻ vị thành niên. Nếu không chuyển được, in ra rằng nó không phải là số. Hãy thử đổi input thành "abc" nữa. Gợi ý: đặt một if / else bình thường bên trong khối if let.
Xem lời giải
fn main() { let input = "17";
if let Ok(age) = input.parse::<u32>() { if age >= 18 { println!("{age} tuổi là người lớn"); } else { println!("{age} tuổi là trẻ vị thành niên"); } } else { println!("'{input}' không phải là số"); }}Kết quả:
17 tuổi là trẻ vị thành niênVới input = "abc", việc chuyển đổi thất bại, nhánh else bên ngoài chạy, và nó in ra 'abc' không phải là số.
Tóm tắt
Phần tiêu đề “Tóm tắt”if let Mẫu = giá_trị { ... }chỉ chạy khối khigiá_trịkhớp vớiMẫu— mọi thứ khác đều lặng lẽ bị bỏ qua.- Thêm
elseđể xử lý trường hợp không khớp, giống mộtifbình thường, và nối nhiều mẫu bằngelse if let. while let Mẫu = giá_trị { ... }lặp chừng nào giá trị còn khớp, rất hợp để lấy hết phần tử khỏi một collection bằng.pop().- Dùng
if let/while letkhi bạn chỉ quan tâm một mẫu; dùngmatchkhi cần xử lý riêng biệt nhiều trường hợp.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Chuyện gì xảy ra khi if let Some(value) = age chạy mà age là None?
2. Khi nào nên dùng match thay vì if let?
3. Trong while let Some(top) = stack.pop() { ... }, khi nào vòng lặp dừng?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- Concise Control Flow with if let and let…else — The Rust Book (tiếng Anh): phần giới thiệu chính thức, có cả let-else.
- if let — Rust by Example (tiếng Anh): ví dụ chạy được với
Optionvà enum của riêng bạn, và vì saoif lethơnmatchkhi chỉ có một mẫu. - while let — Rust by Example (tiếng Anh): một vòng lặp được viết lại từ
loop+matchthànhwhile letngắn gọn hơn. if letpatterns — The Rust Reference (tiếng Anh): các quy tắc chính xác, kể cả cáchif letliên quan tớimatch.