Part 2. Iteration (5 points)
Iteration with iterators is a key concept in Rust. Iteration is similar to Java but has some quirks which can be quite surprising.
For the rest of this lab, you’ll explore iterating over elements in a vector and iterating over characters in a string.
Your task
In a shell, cd
to your assignment repository and create a new project using
cargo
and then open it in VS Code.
$ cargo new iteration
$ code iteration
Change the main
function to
fn main() { let data = vec![2, 3, 5, 7, 11, 13]; for x in data { println!("{x}"); } }
Hover your mouse over the code above and some buttons will appear in the top right of the text box. Click the Run button to run this code.
The vec!
macro creates a new Vec
containing the elements in the
brackets. (Click the link in the previous sentence to go to the documentation
for Vec
.) Vec
is the standard vector class in Rust: it is a growable array
of elements, similar to a list in Python and an ArrayList
in Java. The
vec!
macro here gives a result similar to this (but more efficient and
easier to read).
let mut data = Vec::new(); // Create a new Vec
data.push(2); // Add 2 to the end of the Vec.
data.push(3); // Add 3 to the end of the Vec.
// ...
data.push(13); // Add 13 to the end of the Vec.
Predict what the output of the code will be and then run it. If you predicted incorrectly, try to figure out why the code does what it does before moving on.
Now, duplicate the for loop
fn main() { let data = vec![2, 3, 5, 7, 11, 13]; for x in data { println!("{x}"); } for x in data { println!("{x}"); } }
Again, predict what the output of the code will be and then run it. (You can click the Run button on the above text box to see the error message.)
I suspect the results are quite surprising. You probably got an error that looks like this.
error[E0382]: use of moved value: `data`
--> src/main.rs:8:14
|
2 | let data = vec![2, 3, 5, 7, 11, 13];
| ---- move occurs because `data` has type `Vec<i32>`, which does not implement the `Copy` trait
3 |
4 | for x in data {
| ---- `data` moved due to this implicit call to `.into_iter()`
...
8 | for x in data {
| ^^^^ value used here after move
|
note: `into_iter` takes ownership of the receiver `self`, which moves `data`
--> /Users/steve/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:262:18
|
262 | fn into_iter(self) -> Self::IntoIter;
| ^^^^
help: consider iterating over a slice of the `Vec<i32>`'s content to avoid moving into the `for` loop
|
4 | for x in &data {
| +
For more information about this error, try `rustc --explain E0382`.
error: could not compile `iteration` (bin "iteration") due to previous error
This is a lot of information and it’s quite difficult to understand at first. Reading error messages takes practice. If you look carefully, you’ll notice that the error message is divided into three parts, the error, a note, and a help.
The error tells us that we have tried to use a moved value, namely data
on
line 8, column 14 of the file src/main.rs
. Next, it shows that on line 2 the
variable data
has type std::vec::Vec<i32>
which does not implement Copy
.
Then, on line 4, data
was moved due an implicit call to .into_iter()
and
finally, on line 8, we tried to use data
again.
We’ll talk about what this all means but basically, the first for
loop used
up or consumed our Vec
and thus the Vec
no longer exists. This is not how we normally expect for
loops to work. Nevertheless, this is how Rust works.
The note portion of the error message tells us why it was used up and shows us code from the standard library to explain why. Let’s skip this part for now.
Finally, the help portion offers a way to solve this problem. If we use for x in &data { }
(note the addition of the ampersand &
), this will avoid
consuming data
. Note that it suggests making this change only on the first
for
loop (which for me happens to be on line 4). By using &data
rather
than data
, we’re instructing the loop to iterate over a reference to the
Vec
, rather than the Vec
itself. The upshot is that we can now iterate
multiple times.
Go ahead and make that change and rerun cargo run
. At this point, your code
should print out the list twice.