Step 1 of 7
What an iterator is
An iterator is a value that hands out items one at a time. Every for loop you've written used one behind the scenes. In Rust, an iterator is any type that implements the Iterator trait, which has one required method:
trait Iterator {
type Item; // the type of the items it produces
fn next(&mut self) -> Option<Self::Item>; // the next item, or None when finished
}
type Item is an associated type: each iterator says what kind of item it yields. (You'll implement this trait yourself in the last step.) Here is next called by hand:
fn main() {
let scores = vec![30, 85, 60];
let mut it = scores.iter();
println!("{:?}", it.next());
println!("{:?}", it.next());
println!("{:?}", it.next());
println!("{:?}", it.next());
}
Some(30)
Some(85)
Some(60)
None
scores.iter()creates an iterator that borrows the vector. Its items are&i32references ({:?}prints them like plain numbers).- Each
next()moves the iterator one step forward, so it changes the iterator: the variable must bemut. Nonemeans "no more items". Once a collection's iterator has returnedNone, it's finished.
What a for loop really does
This loop:
for s in &scores {
println!("{s}");
}
is shorthand for:
let mut it = (&scores).into_iter();
while let Some(s) = it.next() {
println!("{s}");
}
for calls into_iter() on whatever you give it, then calls next() until it gets None. That's why you can loop over vectors, arrays, ranges, .chars(), .lines(), maps and sets: they all turn into iterators.
Three ways to iterate a collection
| Call | Items | The collection afterwards | Same as |
|---|---|---|---|
v.iter() |
&T (look) |
unchanged | for x in &v |
v.iter_mut() |
&mut T (change in place) |
changed | for x in &mut v |
v.into_iter() |
T (owned values) |
gone: moved into the loop | for x in v |
fn main() {
let mut prices = vec![10, 20, 30];
for p in prices.iter_mut() {
*p += 1; // change each price in place
}
for p in prices.iter() {
print!("{p} ");
}
println!();
let names = vec![String::from("ann"), String::from("bo")];
for name in names.into_iter() {
let owned: String = name; // each String is ours now
println!("{}", owned.to_uppercase());
}
}
11 21 31
ANN
BO
Common mistakes
- Forgetting
muton an iterator you callnexton:
fn main() {
let v = vec![1, 2, 3];
let it = v.iter();
println!("{:?}", it.next());
}
error[E0596]: cannot borrow `it` as mutable, as it is not declared as mutable
- Using a collection after
into_iter(). It was moved into the iterator:
fn main() {
let names = vec![String::from("Ann"), String::from("Bo")];
for n in names.into_iter() {
println!("{n}");
}
println!("{}", names.len());
}
error[E0382]: borrow of moved value: `names`
Use .iter() (or for n in &names) when you still need the collection afterwards.
Your task
Fill in the blanks: create an iterator over tasks and take its first item with the method that advances an iterator, so the program prints:
Some("write")
Some("test")
Some("ship")
None
Challenge 2
Write fn apply_raise(salaries: &mut [u32], percent: u32) that gives every salary a raise of percent percent, in place, using iter_mut(). Use integer math: the raise for a salary s is s * percent / 100. For example, [1000, 2500] with 10 becomes [1100, 2750].
Challenge 3
An iterator remembers where it stopped. Using one iterator it over lines, call next() once to take the header line and print it as header: .... Then loop over the same iterator with for line in it to print the remaining lines as row: ...:
header: name,score
row: ann,90
row: bo,75