I'm trying to return an iterator without the last element of the input slice, and I need it to work on empty arrays as well. I've coded up two versions:
let all_but_one = if slice.len() > 0 { slice.len() - 1 } else { 0 };
slice.into_iter().take(all_but_one)
// slice.into_iter().take(slice.len()-1) would crash on an empty slice
and
slice.into_iter().rev().skip(1).rev()
I aesthetically like the second better, but I suspect it's slower (especially in the first iteration, since it has to go over the entire list). Is my fear correct? Is there a prettier version that's as cheap as the first?
Instead of the if statement you could use slice.len().saturating_sub(1) to make your first approach with take a bit nicer.
Note that rev is only implemented for iterators that are DoubleEndedIterators, so there is no need to traverse the slice first to get to the end, if that is what you are worried about. I suspect the rev().skip(1).rev() iterator will be pretty well optimized during compilation, but I didn't check.
It's probably less efficient than the slice-specific implementations that have already been listed, but you can also write a lookahead-based implementation that will work for any iterator:
fn all_but_one<T>(iter: impl Iterator<Item=T>)->impl Iterator<Item=T> {
let mut iter = iter.fuse();
let mut cached = iter.next();
std::iter::from_fn(move || {
std::mem::replace(&mut cached, Some(iter.next()?))
})
}