How to iterate through two arrays at once?


#1

Is it possible to iterate through two arrays in a for loop? I tried the following code but I does not compile:

let a = [0, 1, 2, 3, 4];
let mut b = [0; 4];
for (ai, bi) in (a.iter(), b.iter_mut()) {
    *bi = 2 * *ai;
}

Thanks


#2

http://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.zip


#3

Thank you for suggesting that! (sorry I hadn’t found it myself).

Now I have it working:

let a = [0, 1, 2, 3, 4];
let mut b = [0; 4];
for it in a.iter().zip(b.iter_mut()) {
    let (ai, bi) = it;
    *bi = 2 * *ai;
}

However, don’t you think the notation above would have been more clear?

Thanks


#4

Your initial notation would require special language support to recognize if I’m not mistaken. That has to be balanced against clarity and the cost of alternatives. In this case, zip is a pretty standard solution to your problem. (Although perhaps it is more well known to the functional programmers.)


#5

I wonder, if zip should accept IntoIterator instead of Iterator.


#6

What do you mean by “special language support”? Couldn’t it be implemented with impl IntoIterator for (A, B) where A: Iterator, B: Iterator?


#7

Probably. My mistake. I shouldn’t make such claims so early in the morning. :stuck_out_tongue:


#8

It has been discussed and postponed until more thoughts come up in this direction (and probably until variadic generics)

Closed RFC-PR:

Open RFC-Issue:

Internals-discussion:
http://internals.rust-lang.org/t/pre-rfc-replace-iteratorext-zip-with-tuple-iteration/1518


#9

FYI, you can use for (ai, bi) in (and so on) here.


#10

The reddit discussion also.


#11

Thank you very much for directing me to those links, I’ll add my big :+1::+1: to the thread on internals.rust-lang.org.

In my opinion tuple notation would be very convenient and clear. The specific iteration sequence could be specified through brackets nesting.

For instance:

for (x, y, z) in (1..10, 2..11, 3..13) {...}

would give ((1,2,3), (2,3,4) … (9,10,12))

for (x, y, z) in (1..10, (2..11, 3..13)) {...}

would give ((1,2,3), (1,3,4) … (2,2,3), (2,3,4) … (9,10,12))

for (x, y, z) in (1..10, (2..11, (3..13))) {...}

would give ((1,2,3), (1,2,4) … (1,3,3), (1,3,4) … (9,10,12))

This notation in my opinion would be much clearer and compact than using .zip().

I’ll add here another point which is still related to iterating through two arrays. Things get more complex with multidimensional arrays. In this case instead of writing:

for row in m1.iter_mut() { for el in row.iter_mut() {...} }

it would be convenient to be able to write:

for el in m1.iter_mut().iter_mut() {...}

I’ll clarify this point with the following example. I have two 2D arrays, and I need to cycle through each element of them, so I could write for instance:

for i in 0..s {
    for j in 0..s {
        m2[i][j] = m1[i][j].powi(2);
    }
}

To avoid bound checking, I would prefer to use .iter(), but this is what I need to write with the current Rust notation:

for (m1_row, m2_row) in m1.iter().zip(m2.iter_mut()) {
    for (m1_el, m2_el) in m1_row.iter().zip(m2_row.iter_mut()) {
        *m2_el = m1_el.powi(2);
    }
}

This looks quite complex, it would be nice to be able to write:

for (m1_el, m2_el) in (m1.iter().iter(), m2.iter_mut().iter_mut()) {
    *m2_el = m1_el.powi(2);
}

Here is another example:

for i in 0..s {
    for j in 0..s {
        if ((m2[i][j]-m1[i][j])/m1[i][j]).abs() > 1E-6 { test = false; }
    }
}

In this case I could not figure out how to use .iter() at all… (I tried different possibilities but I always get compilation errors one way or another).

Thanks

Edit: now the following code works, I guess something was corrected in the last nightly releases:

for (m1_row, m2_row) in m1.iter().zip(m2.iter()) {
    for (m1_el, m2_el) in m1_row.iter().zip(m2_row.iter()) {
        if ((m2_el-m1_el)/m1_el).abs() > 1E-6 { test = false; }
    }
}


#12

I’m not sure if that would be a very good general solution because you might want to iterate row first or column first. Theoretically, you could do something like this:

// joint_index would return an tuple of iterators which is based
// on the order of numbers. This case would be equivalent to
// `array1[k,i,j]`. `joint_index(0,1,2)` would be `array1[i,j,k]`.
for (a1, a2, a3) in (array1, array2, array3).joint_index(2, 0, 1) {
    if ((a2-a1)/a1).abs() > 1E-6 { test = false; }
}

Having said that, I don’t know how to make your example work.


#13

To specify the iteration order, an optional integer parameter could be added to .iter(), with default 0 indicating iteration over the first dimension available.

To iterate over two dimensions in order (first-dimension, then second-dimension), one could write either:

for el in a2D.iter(0).iter(0) {...}

or:

for el in a2D.iter().iter() {...}

while to iterate in reverse order (second-dimension, then first-dimension), one could write either:

for el in a2D.iter(1).iter(0) {...}

or:

for el in a2D.iter(1).iter() {...}

Considering a 3D array, to iterate in order over second-dimension, first-dimension, third-dimension, one could write either:

for el in a3D.iter(2).iter(0).iter(0) {...}

or:

for el in a3D.iter(2).iter().iter() {...}

#14

I jus tried to use this but am told that iter does not take a parameter.


#15

What @lucatrv was typing was just an idea.

To iterate several iterators at a time we use .zip():

for (a, b) in array1.iter().zip(array2.iter()) {
}