-
Couldn't load subscription status.
- Fork 13.9k
Description
The Iterator::next_chunk (see #98326) implementation for core::iter::Filter does not gracefully drop the elements failing the predicate in the original iterator; instead it forgets them.
As an example, you can try this code:
#![feature(iter_next_chunk)]
struct LoudlyDropped(usize);
impl Drop for LoudlyDropped {
fn drop(&mut self) {
println!("No. {} getting dropped here!!!", self.0);
}
}
fn main() {
let v = (0..10).map(LoudlyDropped).collect::<Vec<_>>();
let _= v.into_iter().filter(|_| false).next_chunk::<1>();
}I expected this program to generate the same message as the program without the last line in main, that is:
#![feature(iter_next_chunk)]
struct LoudlyDropped(usize);
impl Drop for LoudlyDropped {
fn drop(&mut self) {
println!("No. {} getting dropped here!!!", self.0);
}
}
fn main() {
let _ = (0..10).map(LoudlyDropped).collect::<Vec<_>>();
}However, instead, the first program doesn't print anything.
The origin of this behavior is trivial to find: in the lines 90--98 of the file formod core::iter::adapters::filter of the current master (aabbf84), we have the following code (Github permalink), which is part of the implementation of next_chunk:
let result = self.iter.try_for_each(|element| {
let idx = guard.initialized;
guard.initialized = idx + (self.predicate)(&element) as usize;
// SAFETY: Loop conditions ensure the index is in bounds.
unsafe { guard.array.get_unchecked_mut(idx) }.write(element);
if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
});This core::ptr::writes each element that doesn't comply the element into the place out of the range of guard.initialized.
The elements here, if not overwritten by this process, will be left undropped by the Drop implementation of [T; N]::IntoIter.
Meta
Playground version:
Build using the Nightly version: 1.81.0-nightly
(2024-06-22 3cb521a4344f0b556b81)