What's the difference between 'for x in b' and 'for x in b.into_iter()'?

fn main() {
    let mut a = vec!["a", "b", "c"];
    let b = &mut a;
    for x in b {
        *x = "d";
    }
    println!("{:?}", b);
}

This code gives the error, like below.

error[E0382]: use of moved value: `b`
 --> src/main.rs:7:22
  |
4 |     for x in b {
  |              - value moved here
...
7 |     println!("{:?}", b);
  |                      ^ value used here after move
  |
  = note: move occurs because `b` has type `&mut std::vec::Vec<&str>`, which does not implement the `Copy` trait

However, this code works.

fn main() {
    let mut a = vec!["a", "b", "c"];
    let b = &mut a;
    for x in b.into_iter() {
        *x = "d";
    }
    println!("{:?}", b);
}

So, what the difference is?
In some references, what rust's "for" syntax do for types which aren't Iterator seems to be just calling into_iter() (if implemented).

The Iterator doc has all the details, but the gist here is that the for loop calls IntoIterator::into_iter( expression ), so the effect you see is due to the differences in method lookup and ownership when comparing:

  1. IntoIterator::into_iter( expression )
  2. ( expression ).into_iter()
6 Likes

Wow, I did not expect those to behave differently. Can you explain why?

2 Likes

The details I can see at least is that (1) is a function call and (2) is a method call.

  1. Function call
    a. No autoref; if we pass A but &A is expected, no adjustment is done, and instead it is a type error
    b. This function is generic, so we have no clear expected type, so coercions are also not used here
    c. One adjustment is done with function calls: reborrowing. But it's not done here, because the function is generic with a "by-value" parameter. (This part I find a bit confusing)
  2. Method call
    a. Method calls do adjustments like autoref or dereference. If we call into_iter() on an A, it will adjust it to *A, &A, &mut A or etc to what fits the method's receiver. Part of this adjustment is to reborrow instead of consume a mutable reference. This is what we see in the original question.
    b. Adjustments include going through the Deref trait as well, if the first type does not have a matching method.
    c. Adjustments include going through unsizing (finding slice methods for an array and such things). You can see that for [0; 128].into_iter().
11 Likes

Aha, so if you reborrow manually it works:

fn main() {
    let mut a = vec!["a", "b", "c"];
    let b = &mut a;
    for x in &mut *b {
        *x = "d";
    }
    println!("{:?}", b);
}
2 Likes

Thanks for the detailed explanation.
It really helped me.

1 Like