Vòng lặp for trong Rust
for duyệt qua một range (khoảng) hoặc một collection, mỗi lần một phần tử. Trong ba loại vòng lặp của Rust, đây là loại bạn sẽ dùng nhiều nhất.
Duyệt qua một range
Phần tiêu đề “Duyệt qua một range”fn main() { for i in 1..=5 { println!("{i}"); }}Kết quả:
123451..=5 là một range bao gồm cả hai đầu (từ 1 đến 5). Nếu bỏ dấu = — thành 1..5 — nó dừng sớm một bước, cho bạn từ 1 đến 4. Lệch một đơn vị kiểu này rất dễ vấp, nên hãy kiểm tra lại xem bạn muốn dùng loại nào.
Duyệt qua một collection
Phần tiêu đề “Duyệt qua một collection”Đây là lúc for thật sự tỏa sáng — duyệt qua một mảng hay vector mà không cần tự theo dõi chỉ số (index):
fn main() { let fruits = ["táo", "chuối", "anh đào"];
for fruit in fruits { println!("{fruit}"); }}Kết quả:
táochuốianh đàoSo với việc tự đếm bằng vòng lặp while và truy cập theo chỉ số (fruits[i]), cách này ngắn hơn và chẳng có chỉ số nào để viết sai. Bạn sẽ học thêm về mảng và vector trong bài Mảng và Vector.
Lấy cả chỉ số với .enumerate()
Phần tiêu đề “Lấy cả chỉ số với .enumerate()”Nếu bạn cần cả vị trí lẫn giá trị, hãy dùng .enumerate():
fn main() { let fruits = ["táo", "chuối", "anh đào"];
for (index, fruit) in fruits.iter().enumerate() { println!("{index}: {fruit}"); }}Kết quả:
0: táo1: chuối2: anh đàoPhần (index, fruit) là một mẫu tuple: mỗi vòng cho bạn một cặp giá trị, và vòng lặp tách nó ra thành hai biến giúp bạn.
Đếm ngược và nhảy bước
Phần tiêu đề “Đếm ngược và nhảy bước”Nếu bạn muốn đếm ngược từ 3, hoặc chỉ ghé qua mỗi số thứ hai thì sao? Cách thử đầu tiên rất tự nhiên là một range đi ngược, như 3..=1. Nó biên dịch được, nhưng vòng lặp không bao giờ chạy. Range chỉ đếm lên, nên range có điểm đầu lớn hơn điểm cuối đơn giản là rỗng.
Thay vào đó, hãy lấy một range bình thường và đổi cách bạn đi qua nó:
.rev()đi qua range theo chiều ngược lại..step_by(n)lấy mỗi giá trị thứn, bắt đầu từ giá trị đầu tiên.
fn main() { for i in (1..=3).rev() { println!("{i}..."); } println!("Xuất phát!");
for even in (0..=10).step_by(2) { print!("{even} "); } println!();
for i in 3..=1 { println!("dòng này không bao giờ được in: {i}"); }}Kết quả:
3...2...1...Xuất phát!0 2 4 6 8 10Để ý dấu ngoặc tròn: (1..=3).rev(). Nếu thiếu chúng, Rust sẽ đọc 1..=3.rev() thành “từ 1 đến 3.rev()”, không phải điều bạn muốn. Bạn cũng có thể kết hợp cả hai: (0..=10).rev().step_by(5) cho ra 10, 5, 0.
Vì sao nên dùng for thay vì while để đếm
Phần tiêu đề “Vì sao nên dùng for thay vì while để đếm”Bạn có thể viết vòng lặp đếm bằng while, nhưng for với một range loại bỏ cả một nhóm lỗi — không có bộ đếm tự quản lý để lỡ quên cập nhật, và không có nguy cơ chạy quá cuối mảng một bước. Lập trình viên Rust mặc định dùng for và chỉ dùng while khi không có range hay collection nào tự nhiên để duyệt.
Luyện tập
Phần tiêu đề “Luyện tập”1. Bảng cửu chương
Phần tiêu đề “1. Bảng cửu chương”In bảng cửu chương 7 từ 7 x 1 = 7 đến 7 x 10 = 70. Gợi ý: chỉ cần một vòng lặp for qua 1..=10. Bạn có thể nhân ngay bên trong println!.
Xem lời giải
fn main() { for i in 1..=10 { println!("7 x {i} = {}", 7 * i); }}Kết quả:
7 x 1 = 77 x 2 = 147 x 3 = 217 x 4 = 287 x 5 = 357 x 6 = 427 x 7 = 497 x 8 = 567 x 9 = 637 x 10 = 70Biến có tên như {i} có thể đặt thẳng vào trong dấu ngoặc nhọn. Một phép tính như 7 * i thì không, nên nó dùng {} rỗng với giá trị đặt sau dấu phẩy.
2. Cộng các số lẻ
Phần tiêu đề “2. Cộng các số lẻ”Cộng tất cả các số lẻ từ 1 đến 99 và in ra tổng. Gợi ý: (1..100).step_by(2) cho bạn 1, 3, 5, … 99. Giữ một biến tổng mut bên ngoài vòng lặp.
Xem lời giải
fn main() { let mut total = 0;
for n in (1..100).step_by(2) { total += n; }
println!("Tổng các số lẻ từ 1 đến 99 là {total}");}Kết quả:
Tổng các số lẻ từ 1 đến 99 là 2500Một cách khác là duyệt qua 1..100 và chỉ cộng n khi n % 2 == 1. Cả hai đều đúng. step_by chỉ đơn giản là bỏ qua các số chẵn thay vì kiểm tra từng số.
Tóm tắt
Phần tiêu đề “Tóm tắt”for item in range_or_collection { ... }là vòng lặp dùng hằng ngày trong Rust.1..=5bao gồm cả hai đầu;1..5dừng trước 5 một bước.- Duyệt trực tiếp qua mảng/vector để lấy từng giá trị mà không cần tự quản lý chỉ số.
- Dùng
.enumerate()khi bạn cần chỉ số đi kèm với giá trị. - Range ngược như
3..=1là rỗng. Dùng(1..=3).rev()để đếm ngược, và.step_by(n)để nhảy bước.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Trong vòng lặp for, 1..=5 và 1..5 khác nhau thế nào?
2. Làm sao để lấy cả chỉ số lẫn giá trị khi duyệt qua một collection?
3. Vì sao thường nên dùng for thay vì while để đếm qua một collection?
4. for i in 5..=1 { ... } làm gì?
Điểm: 0 / 4
Đọc thêm
Phần tiêu đề “Đọc thêm”- Looping Through a Collection with for — The Rust Book (tiếng Anh): phần giới thiệu chính thức, có cả ví dụ đếm ngược bằng
.rev(). - for and range — Rust by Example (tiếng Anh): các ví dụ chạy được về range, cùng
iter,iter_mutvàinto_itercho collection. - Iterator loops — The Rust Reference (tiếng Anh): các quy tắc chính xác về những gì vòng lặp
forlàm phía sau. Iterator::step_by— tài liệu thư viện chuẩn (tiếng Anh): cáchstep_byhoạt động, kèm ví dụ.revcũng được mô tả trên cùng trang đó.