Rust : Nested For Loops In Rust

Trying to use a nested for loop, works perfectly in most other languages, however in Rust, it is not as straightforward due to the borrow checker.

Problem

    let vec_a = vec![1,2,3,4];
    let vec_b = vec![5,6,7,8];
    
    for a in vec_a {
        for b in vec_b {
         
        }
    }

Discussion

The following code will not compile. The problem is that Vectors are not Copyable, so if we can only move it once.

So where is the vector being moved?

This line here: b in vec_b which under the hood becomes b in vec_b.into_iter(). The signature for into_iter which is defined in the IntoIterator trait:

    fn into_iter(self) -> Self::IntoIter;

Note again, the problem is that into_iter takes self, which consumes the value, in this case it is Vec. Moreover, it does not matter if the underlying type is Copyable.

Solution one

Although Vectors are not Copyable, slices are. Since they are just a pointer and a length, they can be stored on the stack. Our code now looks like:

    let vec_a = vec![1,2,3,4];
    let vec_b = vec![5,6,7,8];
    
    for a in vec_a {
        for b in &vec_b {
         
        }
    }

If you want to be more verbose, you can write vec_b.as_slice.

Discussion

If you do not need to own the items in the slice, then this solution works; you get a reference to an integer and not the actual integer.

Solution two

In most programming languages, you can “copy” anything, as in make an exact copy of something, which can be expensive or cheap depending on what it is you are copying. This notion of “copying” is called Clone in Rust.

The next solution is to use clone since Vectors are Clonable.

    let vec_a = vec![1,2,3,4];
    let vec_b = vec![5,6,7,8];
    
    for a in vec_a {
        for b in vec_b.clone() {
         
        }
    }

Discussion

Each b is now an integer and not a reference to vec_b. For each iteration of vec_a we create a fresh new copy of vec_b. Whether this is expensive in practice depends on your vector size and it’s elements.

Solution three

If you really need to own the data structures in vec_b , but you also want use the most optimal strategy. Then Iterators may be your best solution. Iterators in Rust are lazy and are generally easier to optimise by the compiler. First lets look at a solution using itertools, then a solution without it:

use itertools::Itertools; // 0.9.0

let vec_a = vec![1,2,3,4];
let vec_b = vec![5,6,7,8];

for a in vec_a.into_iter().cartesian_product(vec_b) {
 
}

The output of the for-loop will be:

(1,5)
(1,6)
(1,7)
(1,8)
(2,5)
(2,6)
(2,7)
(2,8)
(3,5)
(3,6)
(3,7)
(3,8)
(4,5)
(4,6)
(4,7)
(4,8)

Each element in vec_a is combined with every element with vec_b.

If you do not want to use itertools, the solution can be done with flat_map and map, however it is a bit more complicated.

The code looks like so:

    let vec_a = vec![1,2,3,4];
    let vec_b = vec![5,6,7,8];
    
    for (a,b) in vec_a.into_iter().flat_map(|a| vec_b.iter().clone().map(move |b| (a, b))) {
            println!("({},{})", a, b)
    }

Discussion

As mentioned above, since Iterators are lazy, this may be the best solution for most usecases. Even moreso, if you can refactor your code to chain iterator methods and delay collecting into a Collection.

Note that the method that doesn’t use itertools, uses quite a few concepts and hinges on the fact that Integers are Copyable; clone only clones the iterator, not the value. If you want to Clone the underlying values, use cloned and not clone. Either way, there is nothing wrong with using the itertools dependency for simplicity!