for (l, h) in (i + 1..)
.zip((0..points.len()).rev())
.take_while(|(l, h)| l < h)
{
points.swap(l, h)
}
though most similar (looking) to the C++ code would be a while loop
let (mut l, mut h) = (i + 1, points.len() - 1);
while l < h {
points.swap(l, h);
l += 1;
h -= 1;
}
Note that indexing in Rust has some bounds-checking overhead compared to C++; so using a library method like reverse will be more similar to the C++ solution performance-wise, while the manual loop might be slightly slower.
To add one more alternative to the mix, for this kind of iteration pattern, you could split up the slice and iterate, reverse the right half iterator, and zip:
let slice = &mut points[i + 1..];
let (left_slice, right_slice) = slice.split_at_mut(slice.len() / 2);
for (l, r) in left_slice.iter_mut().zip(right_slice.iter_mut().rev()) {
std::mem::swap(l, r);
}
You should definitely use the slice reverse method for this. If I'm reading your iteration bounds right, it'd be points[(i + 1)..].reverse().
I went and optimized reverse last year (PR#90821), making sure that -- unlike the CS101 implementations -- it can optimize out all the bounds checks and that LLVM vectorizes it: https://rust.godbolt.org/z/16EaG1rT3.
For that specific case, reverse does the job and is pretty cool.
But what I really wanted to know was how people code that for loop where you initialize two variables and one is incrementing and the other is decrementing.
As usual in Rust, the answer is that you try not to deal with loops where you're manually decrementing or incrementing in the first place. If you look at your request and step back a bit, you have two things you're looping over -- so maybe you want zip -- and one goes in the other direction -- so maybe you want rev.
Combining those two observations, perhaps you'd write it something like this: