OK, so first, like @quinedot mentioned, it's good to do this with non-copy types to really see how the ownership is working so that Rust doesn't implicitly copy stuff and make things look simpler/different than they are.
So let's do this with Strings and look at method 1:
fn main() {
let vec = vec![
"one".to_string(),
"two".to_string(),
"three".to_string(),
"four".to_string(),
];
for v in &vec {
println!("{}", v);
}
}
This fails with the following message:
error[E0507]: cannot move out of a shared reference
--> src/main.rs:9:15
|
9 | for &v in &vec {
| -- ^^^^
| ||
| |data moved here
| |move occurs because `v` has type `std::string::String`, which does not implement the `Copy` trait
| help: consider removing the `&`: `v`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0507`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
So the key to understanding this example is understanding that the section of the for loop between the for and the in keywords is an example of pattern matching.
for &v in vec {
^^ That is a pattern
Pattern matching, as you may know, is the same thing that happens in match statement arms. So in this case, when you are looping over a reference to a vector like this you are getting items that are of the type &String:
for v in vec {
// v is of type `&String`
}
Now when you put a pattern on the left side like this:
for &v in vec {
// ...
}
You are essentially saying that "I expect a reference to something, put that something in v". So putting the reference in the pattern actually strips the reference off of v:
for &v in vec {
// v is of type `String`, the `&` is stripped off of v by the pattern
}
The issue is that now v refers to an owned string, String without an &, but you tried to take that owned string out of a reference, which doesn't work! That's why we get an error message. We can't strip the & off of the string and own it.
To sum it up:
- In method 1,
v is a String, but that doesn't compile
- In method 2,
v is a &String
- In method 3,
v is a String, but it works, because you aren't looping over a reference to vec: &vec.