Is it necessary to create another Vec? Is is possible to use the same Vec, i.e. modify it while iterating?
My solution is, I imagine, inefficient: I allocate a lot of vectors, and I have no guarantee that this will be optimized. Is there a better solution: readable and with less allocation?
One thing you can do which is quite idiomatic is to implement your function as an "iterator adapter" - that is, rather than dealing with Vec
in particular, look at Iterator
s of i32
elements instead. Then everything will be a variable on the stack, and no allocations will be made at all. It could look something like this:
struct DoubleEven<I> {
iter: I,
next: Option<i32>,
}
impl<I> Iterator for DoubleEven<I>
where I: Iterator<Item=i32>
{
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
self.next.take().or_else(||
self.iter.next().map(|value| {
if value % 2 == 0 { self.next = Some(value) }
value
})
)
}
}
Then you can write
fn main() {
let vec = vec![1, 2, 3, 4, 5, 6];
let double_even = DoubleEven {
iter: vec.into_iter(),
next: None,
};
for x in double_even {
print!("{}, ", x) // prints 1, 2, 2, 3, 4, 4, 5, 6, 6,
}
}
Even better, you can add a function double_even
to anything that can be turned into an iterator of i32
, allowing you to write the following:
trait DoubleEvenExt : IntoIterator + Sized {
fn double_even(self) -> DoubleEven<Self::IntoIter> {
DoubleEven {
iter: self.into_iter(),
next: None,
}
}
}
impl<I> DoubleEvenExt for I where I: IntoIterator<Item=i32> {}
fn main() {
let vec = vec![1, 2, 3, 4, 5, 6];
for x in vec.double_even() {
print!("{}, ", x) // prints 1, 2, 2, 3, 4, 4, 5, 6, 6,
}
}
Now I will admit that in this case the boilerplate is adding up, but you can see that at the callsite the code is really very terse. For more complex adapters this pattern can be very useful. In addition, beyond the initial Vec
allocation, there is no memory allocation going on whatsoever! Just stack-allocated variables, allowing for highly-efficient code in a release build.