What is the idiomatic way to make a cartesian product iterator?

let vertices = [bb.start.x, bb.end.x].iter().flat_map(|&x| {
    [bb.start.y, bb.end.y].iter().flat_map(|&y| {
        [bb.start.z, bb.end.z]
            .iter()
            .map(|&z| Vec3::new(x, y, z))
    })
});

The above code complains that temporaries are getting freed too early.

Can I make an iterator that takes ownership of the array? I guess I could chain two iter::Once.

The iterator gets consumed in the same function, so I tried the following code:

let xs = [bb.start.x, bb.end.x];
let ys = [bb.start.y, bb.end.y];
let zs = [bb.start.z, bb.end.z];

let vertices = xs.iter().flat_map(|&x| {
    ys.iter()
        .flat_map(move |&y| zs.iter().map(move |&z| Vec3::new(x, y, z)))
});

But rustc can't infer a lifetime for that.

Vec's into_iter fixes most of my problems. There are issues about array's into_iter. Looks like it has been weird for a long time.

You can use the arrayvec crate which allows creation of owned iterators without heap allocations. Alternately itertools provides a cartesian_product. Basically the issue is that you want to move the integers but not the arrays, which you should also be able to do, but I wasn't able to get it to work in the short time I have before going to bed.

1 Like

Now it compiles but it took some really ugly convincing to make the compiler copy bb.*

let bb2 = bb.clone();
let vertices = vec![bb.start.x, bb.end.x].into_iter().flat_map(move |x| {
    let bb3 = bb2.clone();
    vec![bb2.start.y, bb2.end.y].into_iter().flat_map(move |y| {
        vec![bb3.start.z, bb3.end.z]
            .into_iter()
            .map(move |z| Vec3::new(x, y, z))
    })
});

I have also used clone like this when passing an Arc into threads. Is there any nicer pattern?

Consider:

    let xs = 0..4;
    let ys = 0..2;
    let cross = ys.flat_map(|y| xs.clone().map(move |x| (x, y)));

This is nice and elegant, requiring clone of the inner iterator only because its sequence gets repeated. But integers are a copy type, and copy types are easy. Let's use shared references to a move type instead:

    // The input collections contain custom move types:
    let xs = vec![Size2(1, 1), Size2(2, 2)];
    let ys = vec![Size2(3, 3), Size2(4, 4)];

    // But let's shadow them and work with iterators only.
    let xs = xs.iter();
    let ys = ys.iter();
    let cross = ys.flat_map(|y| xs.clone().map(move |x| (x, y)));

Cool, the same expression works here too, producing pairs of references. Maybe we're done, or maybe we want values instead:

    let cross = ys.flat_map(|y| xs.clone().map(move |x| (x.clone(), y.clone())));

The key here is to work with iterators over the collections, not the collections themselves. Only when producing final outputs do we clone the elements, because naturally they get reused across the product.

3 Likes