Understanding for loop semantics when iterating through a vector containing mutable references

元气小坏坏 提交于 2021-01-28 08:16:42

问题


I am trying to understand why the following code fails:

fn main() {
    let mut a = 10;
    let mut b = 20;
    let mut c = 30;
    let p = vec![&mut a, &mut b, &mut c]; // works with [&a, &b, &c]

    for &x in &p { // works with 'x' instead of of '&x'
        println!("{}", x);
    }
}

The error message is:

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:7:9
  |
7 |     for &x in &p {
  |         ^-
  |         ||
  |         |hint: to prevent move, use `ref x` or `ref mut x`
  |         cannot move out of borrowed content

As I understand, the 'borrowed content' is the mutable references to variables a, b, c by the vec!, but what exactly is getting "moved" here? I presume the move is happening in the beginning of the for loop.

I think there are two mutable references (one by vec) being held, but I guess I am not able to deconstruct &x properly for my understanding and I know the answer lies there. I am able to understand why it works if I put ref x in there as suggested by the compiler or use &&mut x, but I'm not understanding the aforementioned case. (i.e &x).


回答1:


This is a little tricky because bindings in Rust can be a little tricky, but first let's see what we're dealing with and start with some code that does compile:

fn main() {
    let mut a = 10;
    let mut b = 20;
    let mut c = 30;
    let p = vec![&mut a, &mut b, &mut c];

    for x in &p {              // note the lack of &x
        println!("{}", x);
    }
}

This prints out the numbers 10, 20, 30 like you'd expect, but why? Lets change the code to get an error that will tell us what x is:

for x in &p {              // note the lack of &x
    x + ();
}

You then see error[E0369]: binary operation + cannot be applied to type &&mut {integer}

What you get out of iterating over &p is a reference to a mutable reference to an integer. Specifically you get a reference to a vector-owned mutable reference to an integer. The loop cannot obtain a copy of that mutable reference because having two outstanding mutable references is a no-no. If you're not moving that mutable reference out of the vector, the for loop will have to settle for having an immutable borrow of that mutable reference. Here's some code that demonstrates what I'm saying:

let borrow = &p[0];
assert_eq!(borrow, &&mut 10);

// Try to get our own `&mut 10` out of `borrow`    
let gimme = *borrow; // error[E0507]: cannot move out of borrowed content

Now let's talk about what doing for &x in &p does. Here are two equivalent loops that give you the same x and also give you the same error.

for &x in &p {           
}

for temp in &p {
    let x = *temp;
} 

This is because for &x in ... is a destructuring binding. You're asserting "&x matches the structure of an item from iterating over &p. I want x to be the part of that match without the first &."

It's similar to this:

let borrow = &p[0];
assert_eq!(borrow, &&mut 10);

// Try to get our own `&mut 10` out of `borrow`    
let gimme = *borrow; // error[E0507]: cannot move out of borrowed content    
let &gimme_gimme = borrow;  // error[E0507]: cannot move out of borrowed content

In this case, &x matches &&mut {integer} where & matches the first & and x then becomes bound to what's left (&mut {integer}).

I've already explained why you cant have your own copy of that &mut {integer}.



来源:https://stackoverflow.com/questions/49160436/understanding-for-loop-semantics-when-iterating-through-a-vector-containing-muta

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!