Marking the iteration variable as a reference, .iter() vs .iter_mut()


#1

I read that when iterating through an array I can mark the iteration variable as a reference to avoid deferencing it when I use it in the loop:

http://stackoverflow.com/questions/28378407/how-to-iterate-over-an-array-of-integers/28378449#28378449

This works as expected with .iter(), so I can either write:

for ai in a.iter() { println!("{}", *ai); }

or:

for &ai in a.iter() { println!("{}", ai); }

and I get the same result.

However with .iter_mut() I can only write:

for ai in a.iter_mut() { *ai = 2 * *ai; println!("{}", *ai); }

while I get a type mismatch compilation error if I write:

for &ai in a.iter_mut() { ai = 2 * ai; println!("{}", ai); }

Is this expected? In this case it’s confusing…

Thanks


#2

Yes, since the types are different. Try &mut ai.


#3

(I am currently on mobile, so I couldn’t try it myself)


#4

I tried with &mut ai but I still get an error, different one though:

error: re-assignment of immutable variable ai


#5

I don’t think for &a in xxx does what you expect it to do. It copies the value into a new variable. Try it on a non-Copy type.

#[derive(Debug)]
//#[derive(Debug, Copy)]
struct Foo;

fn main () {
    let a = vec![Foo, Foo];
    for &ai in &a { // BAM
        println!("{:?}", ai);
    }
}

So the correct way is to use the deref syntax:

for ai in &mut a {
    *ai = 2 * *ai;
    println!("{}", *ai);
}

#6

I am struggling with variations of this, so say that I have a vectors of tuples and I want to update the first entry of each of them. I found this solution:

let mut v = vec![(0,"a"), (1,"b")];
for &mut (ref mut x, _) in &mut v {
  *x += 1;
}

aren’t there too many muts? is this idiomatic?

cheers

PS: of course I could write v.iter_mut() rather than &mut v, it’s the &mut (ref mut x, _) that annoys me a bit. Is there a way to make it simpler?


#7

I don’t think there is too much you can do, besides keeping it as a tuple:

let mut v = vec![(0,"a"), (1,"b")];
for element in &mut v {
    element.0 += 1;
}

You will have to write a matching pattern if you want to deconstruct it.


#8

I have to admit it’s the first time I see the notation .0, there’s no mention of it in the compound data types chapter of the book.


#9

Hmmmm I thought I specifically added tulle indexing. Open a bug?


#10

“Tuple.” Thanks, iPhone


#11

If grep is not failing me it is only briefly mentioned in http://doc.rust-lang.org/nightly/reference.html#tuple-types

I’ll open a bug.

PS: https://github.com/rust-lang/rust/issues/23962


#12

I’ll correct the misconception in the title, it’s not “Marking the iteration variable as a reference”, you are using Rust’s pattern matching. Any structure you name is deconstructed. Compare the following:

let (x, _) = (1, 2);  // deconstruct a tuple, x is just 1
    
let &y = &5;  // deconstruct a reference (= dereference), y is just the number 5.

So it’s not just “marking as” but “match this structure and deconstruct it”, that is, reach into it and take something out from it.


#13

You are right, thank you for the correction, actually I took that phrase from the following post:

http://stackoverflow.com/questions/28378407/how-to-iterate-over-an-array-of-integers/28378449#28378449

However I still do not understand why neither one of the following two lines work:

for &mut ai in a.iter_mut() { ai = 2 * ai; println!("{}", ai); }

for &mut ai in &mut a { ai = 2 * ai; println!("{}", ai); }

Thanks


#14

I’ll compare with pattern matching in isolation:

let &mut ai = &mut 3;

The pattern matches a mutable reference. And you say that the “contents” of that reference should be bound to the variable ai. The contents is just a number. You dereference the reference to the number this way, and you lose the connection to the original location. If you want to modify the original, you have to modify it through a real reference.

This way: for ai in &mut a { *ai = 2 * (*ai); // because ai is a reference to a number


#15

Ok, so I conclude that the original post on Stack Overflow is wrong, thanks.


#16

It looks correct, but it is only concerned with reading the values one by one. Their code is correct but IMO using the word “marking” is not. Anyway, this case is different: we want to write through the reference that came from the iterator, to change the values in the original array or vector.


#17

Yes, what I would not consider correct IMO is the use of the word “marking” and the fact that the two ways are said to be “equivalent”.


#18

Ok, although I now understand why this is happening, the more I think about this the more I am convinced it should be corrected. Either we make either one of the following expressions:

for &ai in &a {...}
for &ai in a.iter() {...}
for &mut ai in &mut a {...}
for &mut ai in a.iter_mut() {...}

not acceptable, or we make all work as the programmer would have intended to. Otherwise IMO this notation would anyway be unrecommendable, because if I start with for &ai in &a and then realize halfway that I need mutability I need to go back and change all my code.

This similarly applies to patterns as well, see the rust book patterns section:

If you’re matching on a pointer, you can use the same syntax as you declared it with. First, &:

let x = &5;
match x {
    &val => println!("Got a value: {}", val),
}

Here, the val inside the match has type i32. In other words, the left-hand side of the pattern destructures the value. If we have &5, then in &val, val would be 5.


#19

As you see in that quote,

[quote=“lucatrv, post:18, topic:782”]
for &ai in &a
[/quote]destructures the value i.e. moves out of a into ai. Except for Copy types it copies instead. And you can’t move out of a reference. And if you move into ai, there’s no point assigning to ai for your purpose, you clearly want a reference. So, if you don’t want destructuring, don’t do this?

Let’s play with pattern matching a bit more to see why you don’t want this:

fn main() {
    let mut a = 0;
    match &mut a {
        &mut mut x => {
            x = 1;
            println!("a = {}, x = {}", a, x);
        }
    }
}
a = 0, x = 1

#20

That’s a good example, thanks. But I wonder if that would be a good and useful behavior or just misleading for the programmer.
Please notice I’m currently learning Rust, that’s why I find so many observations.