Loops in Rust
Rust has three looping constructs: loop for infinite loops with explicit breaks,
while for condition-driven loops, and for for iterating over ranges and
collections. Each has a distinct purpose, and Rust's iterator system makes for
loops exceptionally powerful.
The loop Keyword
loop creates an infinite loop. It runs the block repeatedly until a break
statement is hit. It is the right tool when you do not know in advance how many
iterations you need — for example, retrying an operation until it succeeds.
fn main() {
let mut count = 0;
loop {
count += 1;
println!("count = {}", count);
if count == 3 {
break; // exit the loop
}
}
println!("Done! count = {}", count);
}count = 1 count = 2 count = 3 Done! count = 3
loop as an Expression (Breaking with a Value)
Like if, loop in Rust is an expression — you can return a value from it
by putting the value after break. This is unique to Rust and extremely useful
for retry logic where you want to capture the result of the successful attempt.
fn main() {
let mut attempt = 0;
// The loop expression evaluates to the value passed to break
let result = loop {
attempt += 1;
if attempt == 3 {
break attempt * 10; // break with a value
}
};
println!("Succeeded on attempt {}, result = {}", attempt, result);
// Succeeded on attempt 3, result = 30
}
// Practical example: keep asking for input until valid
fn get_positive_number(input_sequence: &[i32]) -> i32 {
let mut iter = input_sequence.iter();
loop {
let n = *iter.next().unwrap();
if n > 0 {
break n; // return this value from the loop
}
println!("{} is not positive, try again", n);
}
}
fn demo() {
let numbers = [-1, -3, 0, 5, 8];
let valid = get_positive_number(&numbers);
println!("Got valid number: {}", valid); // 5
}Loop Labels for Nested Loops
When loops are nested, break and continue normally apply to the innermost loop.
Loop labels let you target an outer loop explicitly. Labels start with a single quote
(same syntax as Rust lifetimes).
fn main() {
let mut found = false;
// Label the outer loop
'outer: for i in 0..5 {
for j in 0..5 {
if i + j == 6 {
println!("Found: i={}, j={}", i, j);
found = true;
break 'outer; // break the OUTER loop, not just the inner one
}
}
}
println!("found = {}", found); // found = true
}
fn matrix_search() {
let matrix = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
];
let target = 5;
'search: for (row_idx, row) in matrix.iter().enumerate() {
for (col_idx, &val) in row.iter().enumerate() {
if val == target {
println!("Found {} at row {}, col {}", target, row_idx, col_idx);
break 'search;
}
}
}
}The while Loop
while repeats a block as long as a condition is true. It checks the condition
before each iteration, so if the condition starts out false the body never runs.
fn main() {
// Countdown example
let mut countdown = 5;
while countdown > 0 {
println!("{}...", countdown);
countdown -= 1;
}
println!("Liftoff!");
}
fn collatz(mut n: u64) -> u32 {
let mut steps = 0;
while n != 1 {
if n % 2 == 0 {
n /= 2;
} else {
n = 3 * n + 1;
}
steps += 1;
}
steps
}
fn demo_collatz() {
println!("Collatz steps for 27: {}", collatz(27)); // 111
}5... 4... 3... 2... 1... Liftoff!
while let
while let is a convenient shorthand that keeps looping as long as a pattern
matches. It is frequently used with Option values from iterators or channels.
fn main() {
let mut stack = vec![1, 2, 3, 4, 5];
// Keep popping while there are elements
while let Some(top) = stack.pop() {
println!("Popped: {}", top);
}
println!("Stack is empty");
// Popped: 5, 4, 3, 2, 1
// Equivalent using a regular while:
// while !stack.is_empty() {
// let top = stack.pop().unwrap();
// ...
// }
}The for Loop with Ranges
for with a range is the most common loop for a known number of iterations.
Use .. for exclusive ranges and ..= for inclusive ranges.
fn main() {
// Exclusive range: 0, 1, 2, 3, 4
for i in 0..5 {
print!("{} ", i);
}
println!(); // 0 1 2 3 4
// Inclusive range: 0, 1, 2, 3, 4, 5
for i in 0..=5 {
print!("{} ", i);
}
println!(); // 0 1 2 3 4 5
// Sum 1 to 100
let sum: i32 = (1..=100).sum();
println!("Sum 1..=100 = {}", sum); // 5050
// Reverse a range
for i in (0..5).rev() {
print!("{} ", i);
}
println!(); // 4 3 2 1 0
}for with Iterators
for works with any type that implements the IntoIterator trait.
Rust collections — Vec, arrays, HashMap, etc. — all implement it.
fn main() {
let fruits = vec!["apple", "banana", "cherry"];
// .iter() — borrows each element (&T), collection stays usable after
for fruit in fruits.iter() {
println!("Fruit: {}", fruit);
}
// Shorthand for .iter() — same thing
for fruit in &fruits {
println!("{}", fruit);
}
// .into_iter() — takes ownership, collection is consumed
let numbers = vec![1, 2, 3];
for n in numbers.into_iter() {
println!("{}", n);
}
// numbers cannot be used here — it was moved
// .iter_mut() — mutable references, allows modification in place
let mut scores = vec![70, 85, 90];
for score in scores.iter_mut() {
*score += 5; // dereference to modify
}
println!("{:?}", scores); // [75, 90, 95]
}enumerate() — Index and Value Together
When you need both the index and the value, call .enumerate() on the iterator.
It wraps each element in a (index, value) tuple.
fn main() {
let languages = vec!["Rust", "Go", "Python", "JavaScript"];
for (index, language) in languages.iter().enumerate() {
println!("{}. {}", index + 1, language);
}
// 1. Rust
// 2. Go
// 3. Python
// 4. JavaScript
// Find the position of an element
let target = "Python";
for (i, &lang) in languages.iter().enumerate() {
if lang == target {
println!("{} is at index {}", target, i);
}
}
}zip() — Iterating Two Collections Together
.zip() pairs up two iterators element-by-element, stopping at the shorter one.
It is the idiomatic way to iterate over two collections simultaneously.
fn main() {
let names = vec!["Alice", "Bob", "Carol"];
let scores = vec![95, 87, 92];
for (name, score) in names.iter().zip(scores.iter()) {
println!("{}: {}", name, score);
}
// Alice: 95
// Bob: 87
// Carol: 92
// Zip two ranges
let pairs: Vec<(i32, i32)> = (1..=3).zip(4..=6).collect();
println!("{:?}", pairs); // [(1, 4), (2, 5), (3, 6)]
}step_by() — Custom Step Size
By default, ranges increment by 1. Use .step_by(n) to increment by a different
amount. This is especially useful for working with even/odd numbers or skipping values.
fn main() {
// Even numbers from 0 to 10
for i in (0..=10).step_by(2) {
print!("{} ", i);
}
println!(); // 0 2 4 6 8 10
// Count by 5
for i in (0..=25).step_by(5) {
print!("{} ", i);
}
println!(); // 0 5 10 15 20 25
}break and continue
break exits the loop immediately. continue skips the rest of the current
iteration and jumps to the next one. Both work in loop, while, and for.
fn main() {
// continue — skip even numbers
print!("Odd numbers: ");
for i in 0..10 {
if i % 2 == 0 {
continue; // skip even
}
print!("{} ", i);
}
println!(); // 1 3 5 7 9
// break — stop at first number divisible by 7
print!("Searching: ");
for i in 1..100 {
print!("{} ", i);
if i % 7 == 0 {
println!();
println!("Found first multiple of 7: {}", i);
break;
}
}
}Real Examples
Putting it all together with practical use cases.
fn sum_vector(numbers: &[i32]) -> i32 {
let mut total = 0;
for n in numbers {
total += n;
}
total
// Idiomatic alternative: numbers.iter().sum()
}
fn find_first(haystack: &[i32], needle: i32) -> Option<usize> {
for (i, &val) in haystack.iter().enumerate() {
if val == needle {
return Some(i);
}
}
None
}
fn is_prime(n: u64) -> bool {
if n < 2 { return false; }
if n == 2 { return true; }
if n % 2 == 0 { return false; }
let mut i = 3u64;
while i * i <= n {
if n % i == 0 { return false; }
i += 2;
}
true
}
fn main() {
let nums = vec![3, 1, 4, 1, 5, 9, 2, 6];
println!("Sum: {}", sum_vector(&nums)); // Sum: 31
println!("Index of 9: {:?}", find_first(&nums, 9)); // Index of 9: Some(5)
let primes: Vec<u64> = (2..30).filter(|&n| is_prime(n)).collect();
println!("Primes under 30: {:?}", primes);
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
}Loop Types at a Glance
Loop type | Use when... | Can return value? | Iteration control |
|---|---|---|---|
loop | Repeat indefinitely until a condition inside triggers break | Yes — break with value | break, continue, labels |
while | Repeat while an upfront condition holds | No | break, continue, labels |
while let | Repeat while a pattern continues to match | No | break, continue, labels |
for .. in .. | Iterate over a range or collection | No | break, continue, labels |