`for` Loops

Essential
Last updated: Tags: Rust

for iterates over any value that can be turned into a sequence of items. For straightforward looping it is cleaner than while and more explicit about ownership than the equivalent iterator pipeline.

Iterating over a range

for i in 0..5 {
    println!("{i}"); // 0, 1, 2, 3, 4
}

0..5 is a half-open range: start inclusive, end exclusive. Use 0..=5 to include the endpoint. Ranges are their own type in Rust’s standard library — they implement IntoIterator, so they plug straight into for.

Three ownership flavors

How the loop variable relates to the collection depends on what you pass after in:

FormWhat happens to the collectionElement type
for x in vv is moved (consumed)T
for x in &vv is borrowed immutably&T
for x in &mut vv is borrowed mutably&mut T
let names = vec!["Alice", "Bob", "Carol"];

// borrow — names is still usable after the loop
for name in &names {
    println!("{name}");
}

// move — names is consumed; do not use it after this
for name in names {
    println!("{name}");
}

For a slice &[T] you always iterate as for x in slice (where slice is already a &[T]) — slices do not implement the by-value IntoIterator because they do not own their data.

To mutate elements in place, borrow mutably:

let mut values = vec![1, 2, 3];
for x in &mut values {
    *x *= 2; // dereference to assign through &mut i32
}
// values is now [2, 4, 6]

Pattern destructuring in for

The loop variable is a pattern, not just a name. You can destructure tuples directly:

let pairs = vec![(1, 'a'), (2, 'b'), (3, 'c')];

for (n, c) in &pairs {
    println!("{n}: {c}");
}

enumerate and zip

Two iterator adapters are common enough to use directly with for without building a full pipeline:

let fruits = ["apple", "banana", "cherry"];

// pair each element with its zero-based index
for (i, fruit) in fruits.iter().enumerate() {
    println!("{i}: {fruit}");
}
let nums = [1, 2, 3];
let chars = ['x', 'y', 'z'];

// walk two collections in lockstep; stops at the shorter one
for (n, c) in nums.iter().zip(chars.iter()) {
    println!("{n}-{c}");
}

You will see the full iterator adapter API in Iterators.

break and continue

break exits the loop immediately; continue skips the rest of the current iteration:

for n in 0..20 {
    if n % 2 == 0 { continue; } // skip even numbers
    if n > 10     { break; }    // stop once n exceeds 10
    println!("{n}");
}

Unlike loop, a for loop cannot produce a value via break value — use loop when you need that.