From f33ca44f2a296c88dfe91a5773f89eeb856269b6 Mon Sep 17 00:00:00 2001 From: Mahidul Haque Date: Sat, 19 Sep 2026 16:14:34 +0300 Subject: [PATCH 1/4] Move ExtractIf iterator into its own module --- src/iterators/extract_if.rs | 99 +++++++++++++++++++++++++++++++++++++ src/iterators/mod.rs | 3 ++ src/lib.rs | 94 +---------------------------------- 3 files changed, 103 insertions(+), 93 deletions(-) create mode 100644 src/iterators/extract_if.rs diff --git a/src/iterators/extract_if.rs b/src/iterators/extract_if.rs new file mode 100644 index 00000000..6092ee8a --- /dev/null +++ b/src/iterators/extract_if.rs @@ -0,0 +1,99 @@ +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::{Allocator, SmallVec}; + +/// An iterator which uses a closure to determine if an element should be +/// removed. +/// +/// Returned from [`SmallVec::extract_if`][1]. +/// +/// [1]: struct.SmallVec.html#method.extract_if +pub struct ExtractIf<'a, T, const N: usize, A: Allocator, F> +where F: FnMut(&mut T) -> bool +{ + pub(crate) vec: &'a mut SmallVec, + /// The index of the item that will be inspected by the next call to `next`. + pub(crate) idx: usize, + /// Elements at and beyond this point will be retained. Must be equal or + /// smaller than `old_len`. + pub(crate) end: usize, + /// The number of items that have been drained (removed) thus far. + pub(crate) del: usize, + /// The original length of `vec` prior to draining. + pub(crate) old_len: usize, + /// The filter test predicate. + pub(crate) pred: F +} + +impl core::fmt::Debug for ExtractIf<'_, T, N, A, F> +where + F: FnMut(&mut T) -> bool, + T: core::fmt::Debug +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("ExtractIf") + .field(&self.vec.as_slice()) + .finish() + } +} +impl Iterator for ExtractIf<'_, T, N, A, F> +where F: FnMut(&mut T) -> bool +{ + type Item = T; + + fn next(&mut self) -> Option { + unsafe { + while self.idx < self.end { + let i = self.idx; + // SAFETY: `i < self.end <= self.old_len` + let cur = self.vec.as_mut_ptr().add(i); + let drained = (self.pred)(&mut *cur); + // Update the index *after* the predicate is called. If the + // index is updated prior and the predicate + // panics, the element at this index would be + // leaked. + self.idx += 1; + if drained { + self.del += 1; + return Some(core::ptr::read(cur)); + } else if self.del > 0 { + // SAFETY: `self.del <= i` therefore `i - self.del` is valid + core::ptr::copy_nonoverlapping(cur, cur.sub(self.del), 1); + } + } + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.end - self.idx)) + } +} + +impl Drop for ExtractIf<'_, T, N, A, F> +where F: FnMut(&mut T) -> bool +{ + fn drop(&mut self) { + unsafe { + if self.idx < self.old_len && self.del > 0 { + // This is a pretty messed up state, and there isn't really an + // obviously right thing to do. We don't want to keep trying + // to execute `pred`, so we just backshift all the unprocessed + // elements and tell the vec that they still exist. The + // backshift is required to prevent a + // double-drop of the last successfully + // drained item prior to a panic in the predicate. + let ptr = self.vec.as_mut_ptr(); + let src = ptr.add(self.idx); + let dst = src.sub(self.del); + let tail_len = self.old_len - self.idx; + src.copy_to(dst, tail_len); + } + self.vec.set_len(self.old_len - self.del); + } + } +} diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index d93782ef..ab0fdddf 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -1,2 +1,5 @@ +mod extract_if; +pub use extract_if::ExtractIf; + #[cfg(feature = "rayon")] mod rayon; diff --git a/src/lib.rs b/src/lib.rs index 67634173..72e87fc7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ mod comparisons; mod conversions; mod errors; mod iterators; +pub use iterators::ExtractIf; mod macros; #[cfg(feature = "malloc_size_of")] mod mallocsizeof; @@ -354,99 +355,6 @@ impl Drain<'_, T, N, A> { } } -/// An iterator which uses a closure to determine if an element should be -/// removed. -/// -/// Returned from [`SmallVec::extract_if`][1]. -/// -/// [1]: struct.SmallVec.html#method.extract_if -pub struct ExtractIf<'a, T, const N: usize, A: Allocator, F> -where F: FnMut(&mut T) -> bool -{ - vec: &'a mut SmallVec, - /// The index of the item that will be inspected by the next call to `next`. - idx: usize, - /// Elements at and beyond this point will be retained. Must be equal or - /// smaller than `old_len`. - end: usize, - /// The number of items that have been drained (removed) thus far. - del: usize, - /// The original length of `vec` prior to draining. - old_len: usize, - /// The filter test predicate. - pred: F -} - -impl core::fmt::Debug for ExtractIf<'_, T, N, A, F> -where - F: FnMut(&mut T) -> bool, - T: core::fmt::Debug -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("ExtractIf") - .field(&self.vec.as_slice()) - .finish() - } -} - -impl Iterator for ExtractIf<'_, T, N, A, F> -where F: FnMut(&mut T) -> bool -{ - type Item = T; - - fn next(&mut self) -> Option { - unsafe { - while self.idx < self.end { - let i = self.idx; - // SAFETY: `i < self.end <= self.old_len` - let cur = self.vec.as_mut_ptr().add(i); - let drained = (self.pred)(&mut *cur); - // Update the index *after* the predicate is called. If the - // index is updated prior and the predicate - // panics, the element at this index would be - // leaked. - self.idx += 1; - if drained { - self.del += 1; - return Some(core::ptr::read(cur)); - } else if self.del > 0 { - // SAFETY: `self.del <= i` therefore `i - self.del` is valid - core::ptr::copy_nonoverlapping(cur, cur.sub(self.del), 1); - } - } - None - } - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(self.end - self.idx)) - } -} - -impl Drop for ExtractIf<'_, T, N, A, F> -where F: FnMut(&mut T) -> bool -{ - fn drop(&mut self) { - unsafe { - if self.idx < self.old_len && self.del > 0 { - // This is a pretty messed up state, and there isn't really an - // obviously right thing to do. We don't want to keep trying - // to execute `pred`, so we just backshift all the unprocessed - // elements and tell the vec that they still exist. The - // backshift is required to prevent a - // double-drop of the last successfully - // drained item prior to a panic in the predicate. - let ptr = self.vec.as_mut_ptr(); - let src = ptr.add(self.idx); - let dst = src.sub(self.del); - let tail_len = self.old_len - self.idx; - src.copy_to(dst, tail_len); - } - self.vec.set_len(self.old_len - self.del); - } - } -} - pub struct Splice<'a, I: Iterator + 'a, const N: usize> { drain: Drain<'a, I::Item, N, Global>, replace_with: I From 8afcb7d2a79ae06009c1371916645828a025e709 Mon Sep 17 00:00:00 2001 From: Mahidul Haque Date: Sat, 19 Sep 2026 16:19:45 +0300 Subject: [PATCH 2/4] Use direct ExtractIf module export --- src/iterators/extract_if.rs | 6 ------ src/iterators/mod.rs | 3 +-- src/lib.rs | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/iterators/extract_if.rs b/src/iterators/extract_if.rs index 6092ee8a..958fe2cc 100644 --- a/src/iterators/extract_if.rs +++ b/src/iterators/extract_if.rs @@ -1,9 +1,3 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - use crate::{Allocator, SmallVec}; /// An iterator which uses a closure to determine if an element should be diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index ab0fdddf..7d109599 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -1,5 +1,4 @@ -mod extract_if; -pub use extract_if::ExtractIf; +pub mod extract_if; #[cfg(feature = "rayon")] mod rayon; diff --git a/src/lib.rs b/src/lib.rs index 72e87fc7..c93b5b54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ mod comparisons; mod conversions; mod errors; mod iterators; -pub use iterators::ExtractIf; +pub use iterators::extract_if::ExtractIf; mod macros; #[cfg(feature = "malloc_size_of")] mod mallocsizeof; From 5b944b6c5c86e698dab02ef6e4c8bd4ee0d27b4c Mon Sep 17 00:00:00 2001 From: Mahidul Haque Date: Sat, 19 Sep 2026 16:22:13 +0300 Subject: [PATCH 3/4] Match iterator import formatting --- src/iterators/extract_if.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/iterators/extract_if.rs b/src/iterators/extract_if.rs index 958fe2cc..83b4c852 100644 --- a/src/iterators/extract_if.rs +++ b/src/iterators/extract_if.rs @@ -1,4 +1,7 @@ -use crate::{Allocator, SmallVec}; +use crate::{ + Allocator, + SmallVec +}; /// An iterator which uses a closure to determine if an element should be /// removed. From e66016a7b182dc7e8b771af4285a753ead85de96 Mon Sep 17 00:00:00 2001 From: Mahidul Haque <114881854+Kxrma47@users.noreply.github.com> Date: Sun, 20 Sep 2026 12:47:39 +0300 Subject: [PATCH 4/4] Group iterator exports for rustfmt Signed-off-by: Mahidul Haque <114881854+Kxrma47@users.noreply.github.com> --- src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9ce7ab46..a33fb34b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,8 +20,10 @@ mod comparisons; mod conversions; mod errors; mod iterators; -pub use iterators::drain::Drain; -pub use iterators::extractif::ExtractIf; +pub use iterators::{ + drain::Drain, + extractif::ExtractIf +}; mod macros; #[cfg(feature = "malloc_size_of")] mod mallocsizeof;