Rust — Loops
Three kinds of loops
Rust provides three loop keywords:
| Loop | Use case |
|---|---|
loop | Infinite loop — break out manually |
while | Conditional loop — runs while a condition is true |
for | Iterator loop — runs for each item in a sequence |
Each serves a distinct purpose. Rust has no do-while — loop covers that pattern.
The loop keyword
loop creates an infinite loop that runs until you break:
fn main() {
let mut count = 0;
loop {
count += 1;
println!("Count: {}", count);
if count >= 5 {
break;
}
}
}
Output:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
break exits the loop. Without it, the loop runs forever.
loop returns a value
Unlike loops in most languages, Rust's loop can return a value:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // returns 20
}
};
println!("Result: {}", result); // 20
}
The value after break becomes the loop's result. This is useful for searching or retrying until a condition is met.
Nested loops with labels
When loops are nested, break only exits the innermost loop. Use labels to break out of a specific loop:
fn main() {
let mut count = 0;
'outer: loop {
let mut remaining = 10;
loop {
if remaining == 9 {
break; // exits inner loop
}
if count == 2 {
break 'outer; // exits outer loop
}
remaining -= 1;
}
count += 1;
}
println!("Count: {}", count); // 2
}
Labels start with a single quote 'name and appear before the loop. Use break 'label to exit that specific loop.
The while loop
A while loop runs as long as a condition is true:
fn main() {
let mut number = 5;
while number > 0 {
println!("{}!", number);
number -= 1;
}
println!("Liftoff!");
}
Output:
5!
4!
3!
2!
1!
Liftoff!
while checks the condition before each iteration. If the condition is false initially, the loop never runs.
Avoiding while loops for iteration
Don't use while with a counter for simple iteration — use for instead:
// Works but not idiomatic
let mut i = 0;
while i < 5 {
println!("{}", i);
i += 1;
}
// Idiomatic — use for
for i in 0..5 {
println!("{}", i);
}
for is safer — no off-by-one errors, no forgotten increments, and the compiler optimizes it better.
The for loop
for iterates over anything that implements the Iterator trait:
fn main() {
// Range iteration
for i in 0..5 {
println!("{}", i);
}
// Inclusive range
for i in 1..=5 {
println!("{}", i);
}
// Iterating over an array
let fruits = ["apple", "banana", "cherry"];
for fruit in fruits {
println!("{}", fruit);
}
// Iterating with index
for (index, fruit) in fruits.iter().enumerate() {
println!("{}. {}", index + 1, fruit);
}
}
The for loop is Rust's primary iteration tool. It handles ranges, arrays, vectors, strings, files, and any custom iterator.
Iterator methods
Rust's iterators are powerful — chain methods for filtering, mapping, and collecting:
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Filter even numbers, double them, collect into a new vector
let result: Vec<i32> = numbers
.iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| x * 2)
.collect();
println!("{:?}", result); // [4, 8, 12, 16, 20]
}
Iterators are lazy — they compute values on demand. .collect() triggers the computation and builds the final collection.
break and continue
break exits the loop entirely:
for i in 0..100 {
if i == 5 {
break;
}
println!("{}", i);
}
// Prints: 0 1 2 3 4
continue skips to the next iteration:
for i in 0..10 {
if i % 2 == 0 {
continue; // skip even numbers
}
println!("{}", i);
}
// Prints: 1 3 5 7 9
Both work with labeled loops — break 'outer and continue 'outer.
Common loop patterns
Accumulator
fn main() {
let numbers = vec![10, 20, 30, 40, 50];
let mut sum = 0;
for num in &numbers {
sum += num;
}
println!("Sum: {}", sum); // 150
}
Search with loop
fn main() {
let haystack = vec![3, 7, 1, 9, 4, 6];
let needle = 9;
let mut found = None;
for (i, &val) in haystack.iter().enumerate() {
if val == needle {
found = Some(i);
break;
}
}
match found {
Some(index) => println!("Found {} at index {}", needle, index),
None => println!("Not found"),
}
}
Retry with loop
fn main() {
let mut attempts = 0;
let password = loop {
attempts += 1;
println!("Attempt {} — enter password:", attempts);
// Simulate input
let input = "secret";
if input == "secret" {
break "Access granted";
}
if attempts >= 3 {
break "Access denied";
}
};
println!("{}", password);
}
Infinite loops in real code
loop with break is the standard pattern for event loops, servers, and games:
fn main() {
// Simulated event loop
let events = vec!["click", "scroll", "resize", "quit"];
for event in events {
match event {
"quit" => {
println!("Shutting down");
break;
}
other => println!("Handling: {}", other),
}
}
}
Mini Practice
- Use
loopto calculate factorials — break when you reach 10! - Print the first 20 Fibonacci numbers using a
forloop - Use
whileto simulate a countdown from 10 to 0, printing "Liftoff!" at the end - Iterate over a vector and print only elements greater than 5 using
continue - Use a labeled loop to find the first pair of numbers that multiply to 30
Next: functions — reusable code blocks →
Related Topics
Frequently Asked Questions about Loops
What is Loops in Rust?
Loops is a fundamental concept in Rust. This lesson explains it step by step with clear examples, making it easy for beginners to understand.
How do I learn Loops?
Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Loops.
Why is Loops important in Rust?
Loops is essential for Rust development. Understanding this concept will help you write better code and solve real-world problems more effectively.