Skip to content

<core::iter::Filter as Iterator>::next_chunk virtually core::mem::forgets all elements that fail the predicate #126872

Closed
@gksato

Description

@gksato

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>();
}

(Playground)

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<_>>();
}

(Playground)

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)

Metadata

Metadata

Assignees

Labels

A-iteratorsArea: IteratorsC-bugCategory: This is a bug.requires-nightlyThis issue requires a nightly compiler in some way.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions