How to iterate over an array
Short answer: for item in array hands you each element by value, for item in &array borrows them, and for item in &mut array gives you a &mut so you can change them in place. Reach for &array unless you actually want the elements moved out.
Reading each item
Section titled “Reading each item”In src/main.rs:
fn main() { let scores = [72, 88, 95]; let mut total = 0; for score in &scores { total += score; } println!("total {total}");}cargo run prints:
total 255&scores borrows the array, and the loop variable arrives as an &i32 — one element at a time, borrowed from the array rather than copied out of it. That’s why total += score needs no *: i32 implements AddAssign<&i32>, so adding a borrowed integer to an owned one is a defined operation.
Pass that &i32 somewhere that wants an i32 and the compiler spells the difference out:
fn halve(n: i32) -> i32 { n / 2}
fn main() { let scores: [i32; 3] = [72, 88, 95]; let mut total = 0; for score in &scores { total += halve(score); } println!("total {total}");}cargo build prints:
error[E0308]: mismatched types --> src/main.rs:9:24 |9 | total += halve(score); | ----- ^^^^^ expected `i32`, found `&i32` | | | arguments to this function are incorrect |note: function defined here --> src/main.rs:1:4 |1 | fn halve(n: i32) -> i32 { | ^^^^^ ------help: consider dereferencing the borrow |9 | total += halve(*score); | +
For more information about this error, try `rustc --explain E0308`.The fix is halve(*score), exactly as the help line says. score is a reference to the slot in the array, so *score is the element itself.
If the array arrives as a slice, as in a function taking scores: &[i32], write for score in scores. The & is already in the parameter, and a second one makes &&[i32], which isn’t an iterator.
Taking each item by value
Section titled “Taking each item by value”Drop the & and the loop takes the elements instead of borrowing them:
fn main() { let scores = [72, 88, 95]; let mut total = 0; for score in scores { total += score; } println!("total {total}, scores still {scores:?}");}cargo run prints:
total 255, scores still [72, 88, 95]scores is still usable afterwards, and that isn’t the loop being polite. for score in scores is an implicit call to into_iter(), which takes the array by value, but [i32; 3] implements Copy, so the array is copied into the loop and the original binding keeps its value.
Change the element type to something that isn’t Copy and the same line stops working:
fn main() { let words = [String::from("alpha"), String::from("beta")]; let mut chars = 0; for word in words { chars += word.len(); } println!("{chars} characters in {words:?}");}cargo build prints:
error[E0382]: borrow of moved value: `words` --> src/main.rs:7:38 |2 | let words = [String::from("alpha"), String::from("beta")]; | ----- move occurs because `words` has type `[String; 2]`, which does not implement the `Copy` trait3 | let mut chars = 0;4 | for word in words { | ----- `words` moved due to this implicit call to `.into_iter()`...7 | println!("{chars} characters in {words:?}"); | ^^^^^ value borrowed here after move |note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `words` --> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/iter/traits/collect.rs:312:17help: consider iterating over a slice of the `[String; 2]`'s content to avoid moving into the `for` loop |4 | for word in &words { | +
For more information about this error, try `rustc --explain E0382`.words is gone by the last line. The compiler names the mechanism: for desugars into into_iter(), and that takes ownership of the receiver. Put the & back with for word in &words, and each element arrives as a &String with the array still intact at the end.
Changing each item
Section titled “Changing each item”&mut array is the third form, and it’s the one that writes back:
fn main() { let mut scores = [72, 88, 95]; for score in &mut scores { *score += 5; } println!("{scores:?}");}cargo run prints:
[77, 93, 100]score is a &mut i32, so *score += 5 assigns through the reference and the array changes in place. Two muts are doing different jobs: the one on let mut scores is what allows the array to be borrowed mutably at all, and the one in &mut scores is what the loop needs.
The method forms
Section titled “The method forms”&scores and scores.iter() are the same thing, and so are &mut scores and scores.iter_mut(); the operator forms are sugar over the methods. The third method, scores.into_iter(), is the by-value one: in edition 2024 it hands you the elements themselves rather than references to them.
- What is ownership in Rust? — the rule behind the
E0382above - Why does Rust move values instead of copying them? — why
[String; 2]and[i32; 3]behave differently in the same loop - The bits that trip everyone up — the rest of this chapter