How to rewrite loop that uses indexing to use iterators over a vector

I have been doing some reading and discovered that indexing in Rust is not the same as C++, in Rust there is additional bounds checking when doing my_vec[2] to ensure the index is within bounds. I am writing some performance critical code and have a loop that I currently use

let num_points_in_vector = new_vec.data.len() / 3;
for i in 0..new_vec.data.len()/3 {
        new_vec.data[i] = other_vec[i * 3];
        new_vec.data[num_points_in_vector + i] = other_vec[i * 3 + 1];
        new_vec.data[num_points_in_vector * 2 + i] = other_vec[i * 3 + 2];
    }

I have read that iterators about the most efficient way to loop through a vector. So how would I rewrite this loop to use iterators?

You can use split_at_mut to divide that new_vec into three parts, and other_vec.chunks_exact(3) for the input data, something like:

let num_points_in_vector = new_vec.data.len() / 3;
let (new_slice1, tail) = new_vec.split_at_mut(num_points_in_vector);
let (new_slice2, new_slice3) = tail.split_at_mut(num_points_in_vector);
other_vec.chunks_exact(3)
    .zip(new_slice1)
    .zip(new_slice2)
    .zip(new_slice3)
    .for_each(|(((chunk, x), y), z)| {
        x = chunk[0];
        y = chunk[1];
        z = chunk[2];
    });

Itertools' izip! is nice for dealing with multiple zips too.

2 Likes

sorry for what is likely a dumb question, but I am quite new to Rust and it's concept of zipping. What would the izip! version look like? Thanks in advance!

IIRC, it basically just "flattens" the zip tuples, like:

izip!(other_vec.chunks_exact(3), new_slice1, new_slice2, new_slice3)
    .for_each(|(chunk, x, y, z)| {
        x = chunk[0];
        y = chunk[1];
        z = chunk[2];
    });

You can also stay in for loop with either of these iterators if you prefer:

for (chunk, x, y, z) in izip!(other_vec.chunks_exact(3), new_slice1, new_slice2, new_slice3) {
    x = chunk[0];
    y = chunk[1];
    z = chunk[2];
}

Some iterators like chain and flat_map can do better internal folding with for_each, but I don't think it matters for simple zips.

is there any effective difference between for_each and for? Or is it basically syntactic sugar and it compiles down to the same thing? Thanks for the tips.

A for loop calls Iterator::next repeatedly, whereas for_each is basically a fold with an empty () accumulator. It only makes a difference if the iterator specializes fold to do something better than repeating next() in a loop.

For example, Chain::next has to check its state on every call to see if it's on the first or second part of the chain. But in Chain::fold it can just fold the first part entirely, then fold the second part entirely. Sometimes the compiler optimizer can figure this out in a for loop anyway, "hoisting" the condition out of the loop, but it's more reliable to do that explicitly.

1 Like

Thanks for the tips. Exactly what I need, I will mark yours as the solution.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.