The difference between several ways to display each element in a vector

I recently started to study Rust. Now, I am studying about ownership and references. I want to display each element in a vector. There are three ways to achieve this.

let vec = vec![1,2,3,4,5];

// method 1
for &v in &vec {
    println!("{}", v);
}

// method2
for v in &vec {
    println!("{}", v);
}

//method3
for v in vec {
    println!("{}", v);
}

If I use the method3, ownership of "vec" is moved. I can understand it. But I can't tell the difference between method1 and method2.
Both methods treat "vec" as a reference, so ownership is not moved.
The difference is the reference to "v". I understand that each element in a "vec" is in "v" in the method2, but I do not know what is in "v" in the method1.
In addition to it, I don't know why I get the same result in both methods.

Sorry for the rudimentary question, but can someone tell me about it?

1 Like

... in &vec results in an iterator that returns &isize. With method 1, the &v in the pattern is matched against the &isize from the iterator, and the result is "assign an isize to v on each loop". With method 2, you instead "assign a &isize to v on each loop" -- a reference to each isize within the vector.

Method 1 only works because isize is Copy -- it can copy out the values from the iterator onto v. If you try method 1 on a vector of items that aren't Copy, you'll get an error about moving a non-Copy value. Here's an example on the playground:

2 Likes

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.
1 Like

@quinedot @zicklag Thank you! I understood this problem thanks for your detailed explanation.

1 Like