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

Vòng lặp trong Rust

Rust có ba cách để lặp lại code: loop, while và for. Trang này nói về loop, cách đơn giản và trực tiếp nhất trong ba cách — nó lặp mãi mãi cho tới khi bạn bảo nó dừng.

fn main() {
let mut count = 0;
loop {
count += 1;
println!("{count}");
if count == 3 {
break;
}
}
}

Kết quả:

1
2
3
▶ Tự chạy thử

Bản thân loop không bao giờ dừng — break mới là thứ kết thúc nó. Nếu không có if count == 3 { break; }, chương trình này sẽ in mãi mãi.

Đây là điều loop làm được mà while và for không làm được: bạn có thể đặt một giá trị sau break, và giá trị đó trở thành kết quả của cả vòng lặp:

fn main() {
let mut count = 0;
let result = loop {
count += 1;
if count == 10 {
break count * 2; // dừng lại và trả về một giá trị
}
};
println!("{result}");
}

Kết quả:

20
▶ Tự chạy thử

Cách này tiện cho những việc kiểu “cứ thử lại cho tới khi thành công, rồi đưa tôi kết quả” — bạn sẽ gặp mẫu này trong code thực tế nhiều hơn bạn nghĩ.

Để ý dấu chấm phẩy sau dấu } đóng vòng lặp. Cả khối loop { ... } là vế phải của let result = ...;, nên câu lệnh let cần có ; ở cuối, giống như let x = 5;.

Nếu bạn đến từ ngôn ngữ khác, có thể bạn sẽ viết một vòng lặp vô tận là while true { ... }. Cách đó chạy được trong Rust, nhưng trình biên dịch sẽ cảnh báo bạn:

warning: denote infinite loops with `loop { ... }`
--> src/main.rs:5:5
|
5 | while true {
| ^^^^^^^^^^ help: use `loop`

Cảnh báo này khuyên bạn dùng loop { ... } để biểu thị vòng lặp vô tận. Đây không chỉ là chuyện phong cách. Rust biết rằng thân của một loop luôn luôn chạy, và cách duy nhất để thoát ra là một break. Nó không giả định như vậy với while true, vì nó không xét giá trị của điều kiện. Điều đó tạo ra khác biệt thật sự khi một biến nhận giá trị bên trong vòng lặp:

fn main() {
let mut n = 1;
let answer; // chưa có giá trị
loop {
if n * n > 50 {
answer = n * n; // gán ngay trước khi thoát ra
break;
}
n += 1;
}
println!("Số chính phương đầu tiên lớn hơn 50 là {answer}");
}

Kết quả:

Số chính phương đầu tiên lớn hơn 50 là 64
▶ Tự chạy thử

Với loop, Rust thấy được rằng cách duy nhất để tới println! là đi qua break đó, nên answer luôn được gán giá trị. Đổi loop thành while true và bạn sẽ nhận lỗi error[E0381]: used binding answer is possibly-uninitialized (biến answer có thể chưa được khởi tạo). Trình biên dịch cho rằng vòng lặp có thể kết thúc mà không chạy if lần nào.

Trong thực tế, phiên bản gọn gàng nhất của chương trình này dùng break kèm giá trị, như bạn đã thấy ở trên: let answer = loop { ... break n * n; ... };. Dù theo cách nào, bài học vẫn vậy: khi ý bạn là “mãi mãi, cho tới khi tôi break”, hãy viết loop.

loop là lựa chọn đúng khi bạn không biết trước sẽ lặp bao nhiêu lần — bạn đang chờ một điều kiện nào đó trở thành đúng, chứ không phải đếm qua một khoảng hay một danh sách. Để đếm hoặc duyệt qua một collection, while và for (học ở các bài tiếp theo) thường dễ đọc hơn.

Dùng loop để in một dãy đếm ngược từ 5 về 1, rồi in Phóng!. Gợi ý: bắt đầu với let mut n = 5;, và break khi n về 0.

Xem lời giải
fn main() {
let mut n = 5;
loop {
if n == 0 {
break;
}
println!("{n}...");
n -= 1;
}
println!("Phóng!");
}

Kết quả:

5...
4...
3...
2...
1...
Phóng!
▶ Tự chạy thử

Bước kiểm tra đứng trước, nên khi n bằng 0 vòng lặp dừng lại trước khi in ra 0....

2. Lũy thừa đầu tiên của 2 lớn hơn 1000

Phần tiêu đề “2. Lũy thừa đầu tiên của 2 lớn hơn 1000”

Bắt đầu từ 1 và cứ nhân đôi. Dùng break kèm giá trị để lấy con số đầu tiên lớn hơn 1000, và đếm xem đã nhân đôi bao nhiêu lần. Gợi ý: giữ một biến đếm mut bên ngoài vòng lặp, và viết let power = loop { ... };.

Xem lời giải
fn main() {
let mut value = 1;
let mut doublings = 0;
let power = loop {
value *= 2;
doublings += 1;
if value > 1000 {
break value;
}
};
println!("{power} sau {doublings} lần nhân đôi");
}

Kết quả:

1024 sau 10 lần nhân đôi
▶ Tự chạy thử

break value; kết thúc vòng lặp và trao value cho power. Biến đếm nằm bên ngoài vòng lặp, nên nó giữ nguyên giá trị sau khi vòng lặp kết thúc.

  • loop lặp mãi mãi cho tới khi một break dừng nó lại — luôn đảm bảo có một break.
  • break value; dừng vòng lặp và trả về value làm kết quả của cả biểu thức vòng lặp.
  • Hãy viết loop, không phải while true: Rust biết thân loop luôn chạy, điều này giúp nó kiểm tra các biến của bạn.
  • Dùng loop khi bạn lặp “cho tới khi một điều kiện xảy ra”, không phải khi đếm một số lần đã biết trước.

Kiểm tra nhanh

1. Điều gì giúp một loop trần không chạy mãi mãi?

2. Viết break count * 2; bên trong một vòng lặp có tác dụng gì?

3. Theo trang này, khi nào loop là lựa chọn tốt hơn while hay for?

4. Chuyện gì xảy ra nếu bạn viết while true { ... } trong Rust?

Điểm: 0 / 4