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

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.

fn main() {
for i in 1..=5 {
println!("{i}");
}
}

Kết quả:

1
2
3
4
5
▶ Tự chạy thử

1..=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.

Đâ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áo
chuối
anh đào
▶ Tự chạy thử

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

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áo
1: chuối
2: anh đào
▶ Tự chạy thử

Phầ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.

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
▶ Tự chạy thử

Để ý 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.

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 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70
▶ Tự chạy thử

Biế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.

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à 2500
▶ Tự chạy thử

Mộ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ố.

  • for item in range_or_collection { ... } là vòng lặp dùng hằng ngày trong Rust.
  • 1..=5 bao gồm cả hai đầu; 1..5 dừ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..=1 là rỗng. Dùng (1..=3).rev() để đếm ngược, và .step_by(n) để nhảy bước.

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