Step 3 of 7
Looping: &v, &mut v and v
There are three ways to loop over a vector, and they match the three ways to use any value in Rust: borrow it, borrow it mutably, or take ownership.
fn main() {
let mut prices: Vec<u32> = vec![1200, 450, 999];
let mut total = 0;
for p in &prices {
total += *p;
}
println!("total {total}");
for p in &mut prices {
*p = *p * 9 / 10;
}
println!("{:?}", prices);
for p in prices {
println!("{p}");
}
}
total 2649
[1080, 405, 899]
1080
405
899
| Loop | Each p is |
The vector afterwards |
|---|---|---|
for p in &prices |
&u32, a shared reference |
unchanged, still usable |
for p in &mut prices |
&mut u32, a mutable reference |
changed in place, still usable |
for p in prices |
u32, the value itself |
moved: gone after the loop |
- In the first loop
pis a reference, so*preads the number it points to. - In the second loop
*p = ...writes through the mutable reference into the vector itself (the borrowing module's*). - The third loop consumes the vector. Use it when you're done with the vector and want to move its elements somewhere else.
You'll also see the method forms prices.iter(), prices.iter_mut() and prices.into_iter(); they mean exactly the same three things.
When you need the position too
.iter().enumerate() gives you (index, element) pairs, which a for pattern takes apart:
fn main() {
let runners = vec!["Ana", "Bo", "Cy"];
for (i, name) in runners.iter().enumerate() {
println!("{}. {name}", i + 1);
}
}
1. Ana
2. Bo
3. Cy
That's tidier than for i in 0..runners.len() with runners[i], and clippy suggests it (needless_range_loop). Module 27 covers enumerate and friends properly.
Common mistakes
Using the vector after a consuming loop:
fn main() { let v = vec![1, 2, 3]; for x in v { println!("{x}"); } println!("{}", v.len()); }error[E0382]: borrow of moved value: `v`Loop over
&vif you still needvafterwards.Changing the vector while looping over it. The loop holds a borrow of
v;pushneeds a mutable borrow at the same time:fn main() { let mut v = vec![1, 2, 3]; for x in &v { if *x == 2 { v.push(20); } } }error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutableThis isn't fussiness:
pushmight move the whole buffer to a bigger allocation, and the loop's reference would point at freed memory. In C++ this is a classic crash (iterator invalidation); in Rust it doesn't compile. Collect what you want to add in a second vector and add it after the loop.Forgetting the
*:x += 1wherex: &mut i32fails withbinary assignment operation `+=` cannot be applied to type `&mut i32`. Write*x += 1.
Your task
Write fn apply_raise(salaries: &mut [u32], percent: u32) that raises every salary in place by percent percent (integer math: s * (100 + percent) / 100). Loop over the slice mutably; don't index.
Challenge 2
Write fn clamp_all(values: &mut [i32], low: i32, high: i32) that changes every value below low to low and every value above high to high, in place. (Sensors sometimes report wild numbers; this keeps them in a sane range.)
Challenge 3
Write a program that reads one temperature per line (like 21.5), stores them in a Vec<f64>, then prints the average with one decimal, followed by each reading (one decimal) and whether it's above, below or equal to the average. You need the average before you can label the readings, which is why they have to be stored first.
For the input 21.5, 19.0, 23.5 print:
average: 21.3
21.5 above
19.0 below
23.5 above
Previous: Reading elements with [] and get Next: Insert, remove, swap, truncate, clear