Cornstalks
Cornstalks

Reputation: 38228

Does &mut do anything when declaring a for loop variable?

Consider the following (dumb) program:

fn main() {
    let mut array = &mut [1u8, 2u8, 3u8];
    for &mut value in array {
    }
}

It compiles and runs okay (though warns about unused variables/unnecessary mutability, as expected). But what does &mut do in the for statement?

It doesn't seem to give you a mutable reference into the array, since trying to assign value = 0; results in the error:

error[E0384]: re-assignment of immutable variable `value`

Is &mut here a no-op then?

Upvotes: 2

Views: 667

Answers (2)

Steve Klabnik
Steve Klabnik

Reputation: 15559

So there's a few different things that's going on here. First, here's the answer:

fn main() {
    let mut array = [1u8, 2u8, 3u8];
    for value in &mut array {
        *value = 0; 
   }
}

So. Where did you go wrong? Let's look at what value is, like this:

for &mut value in array {
    let () = value;
}

This gives this error:

  = note: expected type `u8`
  = note:    found type `()`

So here, value is a u8. But why? Well, let's try this one:

for value in array {
    let () = value;
}

This gives:

  = note: expected type `&mut u8`
  = note:    found type `()`

So, value here is an &mut u8, a reference into the array. So by saying for &mut value, we're saying "hey, this is going to be a mutable pointer to a u8. We'd like value to be the u8 value that's pointed at. This is because &mut value is a pattern, which binds against a &mut T and binds value to the T.

So, we remove the &mut, since we don't want a copy of the value, we want to use it to modify what's pointed to. So that looks like this:

fn main() {
    let mut array = &mut [1u8, 2u8, 3u8];
    for value in array {
        *value = 0;
    }
}

This... compiles! Are we done? Well, let's try to print out array, just to be sure:

fn main() {
    let mut array = &mut [1u8, 2u8, 3u8];
    for value in array {
        *value = 0;
    }

    println!("{:?}", array);
}

This fails to compile:

error[E0382]: use of moved value: `array`
 --> <anon>:7:22
  |
3 |     for value in array {
  |                  ----- value moved here

We've destroyed array by iterating. Why's that? Well, when you loop over an array like this, you're saying you want to loop by owner. But that's not actually what we want; we want to loop by mutable reference.

Arrays have a method to help with this:

fn main() {
    let mut array = &mut [1u8, 2u8, 3u8];
    for value in array.iter_mut() {
        *value = 0;
    }

    println!("{:?}", array);
}

iter_mut will iterate by &mut T rather than by T. So this works!

One last thing, though:

let mut array = &mut [1u8, 2u8, 3u8];

This says that array is a &mut [u8; 3], that is, a mutable reference to an array, not an array itself. This probably isn't what you actually want, and it's not neccesary with our code. So we can remove the &mut bit:

let mut array = [1u8, 2u8, 3u8];

And now you're at our first code sample.

Hope this helps!

Upvotes: 9

Shepmaster
Shepmaster

Reputation: 431529

Is &mut here a no-op then?

No, it's part of a pattern. Print the type of value:

fn main() {
    let mut array = &mut [1u8, 2u8, 3u8];

    for &mut value in array {
        let () = value;
        // expected type `u8`
    }

    for value in array {
        let () = value;
        // expected type `&mut u8`
    }
}

Upvotes: 1

Related Questions