Skip to content

Commit 5522939

Browse files
authored
Replace DrainFilter with ExtractIf (#341)
* Replace `DrainFilter` with `ExtractIf` * Use `core::iter::FromIterator` instead of `std::iter::FromIterator`
1 parent defe74d commit 5522939

File tree

3 files changed

+49
-148
lines changed

3 files changed

+49
-148
lines changed

Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ documentation = "https://docs.rs/smallvec/"
1616
write = []
1717
specialization = []
1818
may_dangle = []
19-
drain_filter = []
20-
drain_keep_rest = ["drain_filter"]
19+
extract_if = []
2120

2221
[dependencies]
2322
serde = { version = "1", optional = true, default-features = false }

src/lib.rs

+44-128
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626
//! When this feature is enabled, `SmallVec<u8, _>` implements the `std::io::Write` trait.
2727
//! This feature is not compatible with `#![no_std]` programs.
2828
//!
29-
//! ### `drain_filter`
29+
//! ### `extract_if`
3030
//!
31-
//! **This feature is unstable.** It may change to match the unstable `drain_filter` method in libstd.
31+
//! **This feature is unstable.** It may change to match the unstable `extract_if` method in libstd.
3232
//!
33-
//! Enables the `drain_filter` method, which produces an iterator that calls a user-provided
33+
//! Enables the `extract_if` method, which produces an iterator that calls a user-provided
3434
//! closure to determine which elements of the vector to remove and yield from the iterator.
3535
//!
3636
//! ### `specialization`
@@ -380,13 +380,13 @@ impl<'a, T: 'a, const N: usize> Drop for Drain<'a, T, N> {
380380
}
381381
}
382382

383-
#[cfg(feature = "drain_filter")]
383+
#[cfg(feature = "extract_if")]
384384
/// An iterator which uses a closure to determine if an element should be removed.
385385
///
386-
/// Returned from [`SmallVec::drain_filter`][1].
386+
/// Returned from [`SmallVec::extract_if`][1].
387387
///
388-
/// [1]: struct.SmallVec.html#method.drain_filter
389-
pub struct DrainFilter<'a, T, const N: usize, F>
388+
/// [1]: struct.SmallVec.html#method.extract_if
389+
pub struct ExtractIf<'a, T, const N: usize, F>
390390
where
391391
F: FnMut(&mut T) -> bool,
392392
{
@@ -399,29 +399,23 @@ where
399399
old_len: usize,
400400
/// The filter test predicate.
401401
pred: F,
402-
/// A flag that indicates a panic has occurred in the filter test predicate.
403-
/// This is used as a hint in the drop implementation to prevent consumption
404-
/// of the remainder of the `DrainFilter`. Any unprocessed items will be
405-
/// backshifted in the `vec`, but no further items will be dropped or
406-
/// tested by the filter predicate.
407-
panic_flag: bool,
408402
}
409403

410-
#[cfg(feature = "drain_filter")]
411-
impl<T, const N: usize, F> core::fmt::Debug for DrainFilter<'_, T, N, F>
404+
#[cfg(feature = "extract_if")]
405+
impl<T, const N: usize, F> core::fmt::Debug for ExtractIf<'_, T, N, F>
412406
where
413407
F: FnMut(&mut T) -> bool,
414408
T: core::fmt::Debug,
415409
{
416410
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
417-
f.debug_tuple("DrainFilter")
411+
f.debug_tuple("ExtractIf")
418412
.field(&self.vec.as_slice())
419413
.finish()
420414
}
421415
}
422416

423-
#[cfg(feature = "drain_filter")]
424-
impl<T, F, const N: usize> Iterator for DrainFilter<'_, T, N, F>
417+
#[cfg(feature = "extract_if")]
418+
impl<T, F, const N: usize> Iterator for ExtractIf<'_, T, N, F>
425419
where
426420
F: FnMut(&mut T) -> bool,
427421
{
@@ -432,9 +426,7 @@ where
432426
while self.idx < self.old_len {
433427
let i = self.idx;
434428
let v = core::slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
435-
self.panic_flag = true;
436429
let drained = (self.pred)(&mut v[i]);
437-
self.panic_flag = false;
438430
// Update the index *after* the predicate is called. If the index
439431
// is updated prior and the predicate panics, the element at this
440432
// index would be leaked.
@@ -444,8 +436,8 @@ where
444436
return Some(core::ptr::read(&v[i]));
445437
} else if self.del > 0 {
446438
let del = self.del;
447-
let src: *const Self::Item = &v[i];
448-
let dst: *mut Self::Item = &mut v[i - del];
439+
let src: *const T = &v[i];
440+
let dst: *mut T = &mut v[i - del];
449441
core::ptr::copy_nonoverlapping(src, dst, 1);
450442
}
451443
}
@@ -458,109 +450,27 @@ where
458450
}
459451
}
460452

461-
#[cfg(feature = "drain_filter")]
462-
impl<T, F, const N: usize> Drop for DrainFilter<'_, T, N, F>
453+
#[cfg(feature = "extract_if")]
454+
impl<T, F, const N: usize> Drop for ExtractIf<'_, T, N, F>
463455
where
464456
F: FnMut(&mut T) -> bool,
465457
{
466458
fn drop(&mut self) {
467-
struct BackshiftOnDrop<'a, 'b, T, const N: usize, F>
468-
where
469-
F: FnMut(&mut T) -> bool,
470-
{
471-
drain: &'b mut DrainFilter<'a, T, N, F>,
472-
}
473-
474-
impl<'a, 'b, T, const N: usize, F> Drop for BackshiftOnDrop<'a, 'b, T, N, F>
475-
where
476-
F: FnMut(&mut T) -> bool,
477-
{
478-
fn drop(&mut self) {
479-
unsafe {
480-
if self.drain.idx < self.drain.old_len && self.drain.del > 0 {
481-
// This is a pretty messed up state, and there isn't really an
482-
// obviously right thing to do. We don't want to keep trying
483-
// to execute `pred`, so we just backshift all the unprocessed
484-
// elements and tell the vec that they still exist. The backshift
485-
// is required to prevent a double-drop of the last successfully
486-
// drained item prior to a panic in the predicate.
487-
let ptr = self.drain.vec.as_mut_ptr();
488-
let src = ptr.add(self.drain.idx);
489-
let dst = src.sub(self.drain.del);
490-
let tail_len = self.drain.old_len - self.drain.idx;
491-
src.copy_to(dst, tail_len);
492-
}
493-
self.drain.vec.set_len(self.drain.old_len - self.drain.del);
494-
}
495-
}
496-
}
497-
498-
let backshift = BackshiftOnDrop { drain: self };
499-
500-
// Attempt to consume any remaining elements if the filter predicate
501-
// has not yet panicked. We'll backshift any remaining elements
502-
// whether we've already panicked or if the consumption here panics.
503-
if !backshift.drain.panic_flag {
504-
backshift.drain.for_each(drop);
505-
}
506-
}
507-
}
508-
509-
#[cfg(feature = "drain_keep_rest")]
510-
impl<T, F, const N: usize> DrainFilter<'_, T, N, F>
511-
where
512-
F: FnMut(&mut T) -> bool,
513-
{
514-
/// Keep unyielded elements in the source `Vec`.
515-
///
516-
/// # Examples
517-
///
518-
/// ```
519-
/// # use smallvec::{smallvec, SmallVec};
520-
///
521-
/// let mut vec: SmallVec<char, 2> = smallvec!['a', 'b', 'c'];
522-
/// let mut drain = vec.drain_filter(|_| true);
523-
///
524-
/// assert_eq!(drain.next().unwrap(), 'a');
525-
///
526-
/// // This call keeps 'b' and 'c' in the vec.
527-
/// drain.keep_rest();
528-
///
529-
/// // If we wouldn't call `keep_rest()`,
530-
/// // `vec` would be empty.
531-
/// assert_eq!(vec, SmallVec::<char, 2>::from_slice(&['b', 'c']));
532-
/// ```
533-
pub fn keep_rest(self) {
534-
// At this moment layout looks like this:
535-
//
536-
// _____________________/-- old_len
537-
// / \
538-
// [kept] [yielded] [tail]
539-
// \_______/ ^-- idx
540-
// \-- del
541-
//
542-
// Normally `Drop` impl would drop [tail] (via .for_each(drop), ie still calling `pred`)
543-
//
544-
// 1. Move [tail] after [kept]
545-
// 2. Update length of the original vec to `old_len - del`
546-
// a. In case of ZST, this is the only thing we want to do
547-
// 3. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do
548-
let mut this = ManuallyDrop::new(self);
549-
550459
unsafe {
551-
// ZSTs have no identity, so we don't need to move them around.
552-
let needs_move = core::mem::size_of::<T>() != 0;
553-
554-
if needs_move && this.idx < this.old_len && this.del > 0 {
555-
let ptr = this.vec.as_mut_ptr();
556-
let src = ptr.add(this.idx);
557-
let dst = src.sub(this.del);
558-
let tail_len = this.old_len - this.idx;
460+
if self.idx < self.old_len && self.del > 0 {
461+
// This is a pretty messed up state, and there isn't really an
462+
// obviously right thing to do. We don't want to keep trying
463+
// to execute `pred`, so we just backshift all the unprocessed
464+
// elements and tell the vec that they still exist. The backshift
465+
// is required to prevent a double-drop of the last successfully
466+
// drained item prior to a panic in the predicate.
467+
let ptr = self.vec.as_mut_ptr();
468+
let src = ptr.add(self.idx);
469+
let dst = src.sub(self.del);
470+
let tail_len = self.old_len - self.idx;
559471
src.copy_to(dst, tail_len);
560472
}
561-
562-
let new_len = this.old_len - this.del;
563-
this.vec.set_len(new_len);
473+
self.vec.set_len(self.old_len - self.del);
564474
}
565475
}
566476
}
@@ -961,11 +871,18 @@ impl<T, const N: usize> SmallVec<T, N> {
961871
}
962872
}
963873

964-
#[cfg(feature = "drain_filter")]
874+
#[cfg(feature = "extract_if")]
965875
/// Creates an iterator which uses a closure to determine if an element should be removed.
966876
///
967-
/// If the closure returns true, the element is removed and yielded. If the closure returns
968-
/// false, the element will remain in the vector and will not be yielded by the iterator.
877+
/// If the closure returns true, the element is removed and yielded.
878+
/// If the closure returns false, the element will remain in the vector and will not be yielded
879+
/// by the iterator.
880+
///
881+
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
882+
/// or the iteration short-circuits, then the remaining elements will be retained.
883+
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
884+
///
885+
/// [`retain`]: SmallVec::retain
969886
///
970887
/// Using this method is equivalent to the following code:
971888
/// ```
@@ -984,11 +901,11 @@ impl<T, const N: usize> SmallVec<T, N> {
984901
///
985902
/// # assert_eq!(vec, SmallVec::<i32, 8>::from_slice(&[1i32, 4, 5]));
986903
/// ```
987-
/// ///
988-
/// But `drain_filter` is easier to use. `drain_filter` is also more efficient,
904+
///
905+
/// But `extract_if` is easier to use. `extract_if` is also more efficient,
989906
/// because it can backshift the elements of the array in bulk.
990907
///
991-
/// Note that `drain_filter` also lets you mutate every element in the filter closure,
908+
/// Note that `extract_if` also lets you mutate every element in the filter closure,
992909
/// regardless of whether you choose to keep or remove it.
993910
///
994911
/// # Examples
@@ -999,13 +916,13 @@ impl<T, const N: usize> SmallVec<T, N> {
999916
/// # use smallvec::SmallVec;
1000917
/// let mut numbers: SmallVec<i32, 16> = SmallVec::from_slice(&[1i32, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]);
1001918
///
1002-
/// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::<SmallVec<i32, 16>>();
919+
/// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::<SmallVec<i32, 16>>();
1003920
/// let odds = numbers;
1004921
///
1005922
/// assert_eq!(evens, SmallVec::<i32, 16>::from_slice(&[2i32, 4, 6, 8, 14]));
1006923
/// assert_eq!(odds, SmallVec::<i32, 16>::from_slice(&[1i32, 3, 5, 9, 11, 13, 15]));
1007924
/// ```
1008-
pub fn drain_filter<F>(&mut self, filter: F) -> DrainFilter<'_, T, N, F>
925+
pub fn extract_if<F>(&mut self, filter: F) -> ExtractIf<'_, T, N, F>
1009926
where
1010927
F: FnMut(&mut T) -> bool,
1011928
{
@@ -1016,13 +933,12 @@ impl<T, const N: usize> SmallVec<T, N> {
1016933
self.set_len(0);
1017934
}
1018935

1019-
DrainFilter {
936+
ExtractIf {
1020937
vec: self,
1021938
idx: 0,
1022939
del: 0,
1023940
old_len,
1024941
pred: filter,
1025-
panic_flag: false,
1026942
}
1027943
}
1028944

src/tests.rs

+4-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{smallvec, SmallVec};
22

3-
use std::iter::FromIterator;
3+
use core::iter::FromIterator;
44

55
use alloc::borrow::ToOwned;
66
use alloc::boxed::Box;
@@ -1060,27 +1060,13 @@ fn test_clone_from() {
10601060
assert_eq!(&*b, &[20, 21, 22]);
10611061
}
10621062

1063-
#[cfg(feature = "drain_filter")]
1063+
#[cfg(feature = "extract_if")]
10641064
#[test]
1065-
fn drain_filter() {
1065+
fn test_extract_if() {
10661066
let mut a: SmallVec<u8, 2> = smallvec![1u8, 2, 3, 4, 5, 6, 7, 8];
10671067

1068-
let b: SmallVec<u8, 2> = a.drain_filter(|x| *x % 3 == 0).collect();
1068+
let b: SmallVec<u8, 2> = a.extract_if(|x| *x % 3 == 0).collect();
10691069

10701070
assert_eq!(a, SmallVec::<u8, 2>::from_slice(&[1u8, 2, 4, 5, 7, 8]));
10711071
assert_eq!(b, SmallVec::<u8, 2>::from_slice(&[3u8, 6]));
10721072
}
1073-
1074-
#[cfg(feature = "drain_keep_rest")]
1075-
#[test]
1076-
fn drain_keep_rest() {
1077-
let mut a: SmallVec<i32, 3> = smallvec![1i32, 2, 3, 4, 5, 6, 7, 8];
1078-
let mut df = a.drain_filter(|x| *x % 2 == 0);
1079-
1080-
assert_eq!(df.next().unwrap(), 2);
1081-
assert_eq!(df.next().unwrap(), 4);
1082-
1083-
df.keep_rest();
1084-
1085-
assert_eq!(a, SmallVec::<i32, 3>::from_slice(&[1i32, 3, 5, 6, 7, 8]));
1086-
}

0 commit comments

Comments
 (0)