pub struct Bomb;
impl Drop for Bomb {
fn drop(&mut self) { panic!("dropped") }
}
pub fn test(items: Vec<(i32, Option<Bomb>)>) -> i32 {
let mut last = 0;
for (x, bomb) in items {
last = x;
std::mem::forget(bomb);
}
last
}
In the test function, the loop always fully consumes the vector, but the nightly compiler still generates another loop after the written loop for dropping the remaining items (there should be none). Is there any way to stop the compiler from generating it?
Interestingly, changing Option<Bomb> to Bomb stopped the compiler from generating the unnecessary drop code. Why does having an Option in the element type blocked this optimization?
I think the second loop is for dropping the std::vec::IntoIter iterator. Using Option<Bomb> instead of Bomb likely causes things to be perturbed just enough that LLVM fails to optimize it away. Unfortunately every optimizer has edge cases where it fails to optimize even though something that is almost the same correctly optimizes.
As general advice, if a for loop is producing poor machine code, you can try for_each() instead:
pub fn test(items: Vec<(i32, Option<Bomb>)>) -> i32 {
let mut last = 0;
items.into_iter().for_each(|(x, bomb)| {
last = x;
std::mem::forget(bomb);
}
last
}
Unfortunately, it seems to make no difference in this case at all. I looked at the code and vec::IntoIter does not have any provision for taking advantage of knowing that it’s consuming the iterator. I wonder if this is easy to fix.