Rust iterators optimally

Right now I learn rust through small ray tracer.
I write algorithm that detect collision between ray and convex polygon.
For the algorithm to work some data should be prepared in advance. The polygon is defined as list of 3d points and from this I need to create list of vectors like
vecs[i] = points[i] - points[(i + 1)%points.len()]
I need points and vecs to be static arrays

This is my current implementation

        let mut directed_sides : [Vector3<f32>; 3] = [Vector3::new(0.0, 0.0, 0.0); 3];
        for i in 0..points.len() {
            let begin_index = i;
            let end_index = (i + 1) % points.len();
            directed_sides[begin_index] = (points[end_index] - points[begin_index]).normalize();
        }

How I can achieve such behavior with iterators and combinators (aka map, filter and so on)

If you want wrapping like that, consider https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.cycle.

If you want adjacent things, consider https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.tuple_windows.

4 Likes

Thanks for your answer. It's not clear for me how I can achieve the exact behavior with window and cycle.
More exactly I don't understand how with cycle I can achieve len + 1 iterations.
I also don't understand what should be the generic parameter of collect.
Initially I believed it should be the type of the element, but now I believe it should be the type the final container like array<element_type> or vector<element_type>

I guess something like this (simplified)

for ((p1, p2), d) in points.iter().circular_tuple_windows().zip(directed_sides.iter_mut()) {
    *d = (p2 - p1).normalize();
}

can you also explain how the collect method work. I also want to know if I can do the same with map.
Also how sure we are if the compiler will optimize away all of the stuff

With regards to your use case, collect method is not necessary.

Possibly. You can get rid of circular_tuple_windows via:

let iter = points.iter().skip(1).chain(points.first());
for ((p1, p2), d) in points.iter().zip(iter)
    .zip(directed_sides.iter_mut())
{
    *d = p2 - p1;
}

Or let iter = points.iter().cycle().skip(1); from the advice of @scottmcm .

Iterator is a zero cost abstraction, but I haven't dug into it yet.

1 Like

Well, if the length of points varies, you may want this:

let iter = points.iter().cycle().skip(1);
let directed_sides: Vec<_> = points.iter().zip(iter)
    .map(|(p1, p2)| p2 - p1)
    .collect();
1 Like