Skip to content

Commit a8d7dc5

Browse files
committed
Add range support to extract_if
1 parent 5aa1b83 commit a8d7dc5

File tree

6 files changed

+65
-43
lines changed

6 files changed

+65
-43
lines changed

src/map.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,8 @@ impl<K, V, S> IndexMap<K, V, S> {
304304
Drain::new(self.core.drain(range))
305305
}
306306

307-
/// Creates an iterator which uses a closure to determine if an element should be removed.
307+
/// Creates an iterator which uses a closure to determine if an element should be removed,
308+
/// for all elements in the given range.
308309
///
309310
/// If the closure returns true, the element is removed from the map and yielded.
310311
/// If the closure returns false, or panics, the element remains in the map and will not be
@@ -313,6 +314,11 @@ impl<K, V, S> IndexMap<K, V, S> {
313314
/// Note that `extract_if` lets you mutate every value in the filter closure, regardless of
314315
/// whether you choose to keep or remove it.
315316
///
317+
/// The range may be any type that implements [`RangeBounds<usize>`],
318+
/// including all of the `std::ops::Range*` types, or even a tuple pair of
319+
/// `Bound` start and end values. To check the entire map, use `RangeFull`
320+
/// like `map.extract_if(.., predicate)`.
321+
///
316322
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
317323
/// or the iteration short-circuits, then the remaining elements will be retained.
318324
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
@@ -327,19 +333,20 @@ impl<K, V, S> IndexMap<K, V, S> {
327333
/// use indexmap::IndexMap;
328334
///
329335
/// let mut map: IndexMap<i32, i32> = (0..8).map(|x| (x, x)).collect();
330-
/// let extracted: IndexMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect();
336+
/// let extracted: IndexMap<i32, i32> = map.extract_if(.., |k, _v| k % 2 == 0).collect();
331337
///
332338
/// let evens = extracted.keys().copied().collect::<Vec<_>>();
333339
/// let odds = map.keys().copied().collect::<Vec<_>>();
334340
///
335341
/// assert_eq!(evens, vec![0, 2, 4, 6]);
336342
/// assert_eq!(odds, vec![1, 3, 5, 7]);
337343
/// ```
338-
pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F>
344+
pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, F>
339345
where
340346
F: FnMut(&K, &mut V) -> bool,
347+
R: RangeBounds<usize>,
341348
{
342-
ExtractIf::new(&mut self.core, pred)
349+
ExtractIf::new(&mut self.core, range, pred)
343350
}
344351

345352
/// Splits the collection into two at the given index.

src/map/core/extract.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,46 @@
11
#![allow(unsafe_code)]
22

33
use super::{Bucket, IndexMapCore};
4+
use crate::util::simplify_range;
5+
6+
use core::ops::RangeBounds;
47

58
impl<K, V> IndexMapCore<K, V> {
6-
pub(crate) fn extract(&mut self) -> ExtractCore<'_, K, V> {
9+
pub(crate) fn extract<R>(&mut self, range: R) -> ExtractCore<'_, K, V>
10+
where
11+
R: RangeBounds<usize>,
12+
{
13+
let range = simplify_range(range, self.entries.len());
14+
715
// SAFETY: We must have consistent lengths to start, so that's a hard assertion.
8-
// Then the worst `set_len(0)` can do is leak items if `ExtractCore` doesn't drop.
16+
// Then the worst `set_len` can do is leak items if `ExtractCore` doesn't drop.
917
assert_eq!(self.entries.len(), self.indices.len());
1018
unsafe {
11-
self.entries.set_len(0);
19+
self.entries.set_len(range.start);
1220
}
1321
ExtractCore {
1422
map: self,
15-
current: 0,
16-
new_len: 0,
23+
new_len: range.start,
24+
current: range.start,
25+
end: range.end,
1726
}
1827
}
1928
}
2029

2130
pub(crate) struct ExtractCore<'a, K, V> {
2231
map: &'a mut IndexMapCore<K, V>,
23-
current: usize,
2432
new_len: usize,
33+
current: usize,
34+
end: usize,
2535
}
2636

2737
impl<K, V> Drop for ExtractCore<'_, K, V> {
2838
fn drop(&mut self) {
2939
let old_len = self.map.indices.len();
3040
let mut new_len = self.new_len;
41+
3142
debug_assert!(new_len <= self.current);
43+
debug_assert!(self.current <= self.end);
3244
debug_assert!(self.current <= old_len);
3345
debug_assert!(old_len <= self.map.entries.capacity());
3446

@@ -62,13 +74,12 @@ impl<K, V> ExtractCore<'_, K, V> {
6274
where
6375
F: FnMut(&mut Bucket<K, V>) -> bool,
6476
{
65-
let old_len = self.map.indices.len();
66-
debug_assert!(old_len <= self.map.entries.capacity());
77+
debug_assert!(self.end <= self.map.entries.capacity());
6778

6879
let base = self.map.entries.as_mut_ptr();
69-
while self.current < old_len {
80+
while self.current < self.end {
7081
// SAFETY: We're maintaining both indices within bounds of the original entries, so
71-
// 0..new_len and current..old_len are always valid items for our Drop to keep.
82+
// 0..new_len and current..indices.len() are always valid items for our Drop to keep.
7283
unsafe {
7384
let item = base.add(self.current);
7485
if pred(&mut *item) {
@@ -91,6 +102,6 @@ impl<K, V> ExtractCore<'_, K, V> {
91102
}
92103

93104
pub(crate) fn remaining(&self) -> usize {
94-
self.map.indices.len() - self.current
105+
self.end - self.current
95106
}
96107
}

src/map/iter.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -778,21 +778,19 @@ where
778778
///
779779
/// This `struct` is created by [`IndexMap::extract_if()`].
780780
/// See its documentation for more.
781-
pub struct ExtractIf<'a, K, V, F>
782-
where
783-
F: FnMut(&K, &mut V) -> bool,
784-
{
781+
pub struct ExtractIf<'a, K, V, F> {
785782
inner: ExtractCore<'a, K, V>,
786783
pred: F,
787784
}
788785

789-
impl<K, V, F> ExtractIf<'_, K, V, F>
790-
where
791-
F: FnMut(&K, &mut V) -> bool,
792-
{
793-
pub(super) fn new(core: &mut IndexMapCore<K, V>, pred: F) -> ExtractIf<'_, K, V, F> {
786+
impl<K, V, F> ExtractIf<'_, K, V, F> {
787+
pub(super) fn new<R>(core: &mut IndexMapCore<K, V>, range: R, pred: F) -> ExtractIf<'_, K, V, F>
788+
where
789+
R: RangeBounds<usize>,
790+
F: FnMut(&K, &mut V) -> bool,
791+
{
794792
ExtractIf {
795-
inner: core.extract(),
793+
inner: core.extract(range),
796794
pred,
797795
}
798796
}
@@ -820,7 +818,8 @@ where
820818

821819
impl<'a, K, V, F> fmt::Debug for ExtractIf<'a, K, V, F>
822820
where
823-
F: FnMut(&K, &mut V) -> bool,
821+
K: fmt::Debug,
822+
V: fmt::Debug,
824823
{
825824
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826825
f.debug_struct("ExtractIf").finish_non_exhaustive()

src/set.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,18 @@ impl<T, S> IndexSet<T, S> {
249249
Drain::new(self.map.core.drain(range))
250250
}
251251

252-
/// Creates an iterator which uses a closure to determine if a value should be removed.
252+
/// Creates an iterator which uses a closure to determine if a value should be removed,
253+
/// for all values in the given range.
253254
///
254255
/// If the closure returns true, then the value is removed and yielded.
255256
/// If the closure returns false, the value will remain in the list and will not be yielded
256257
/// by the iterator.
257258
///
259+
/// The range may be any type that implements [`RangeBounds<usize>`],
260+
/// including all of the `std::ops::Range*` types, or even a tuple pair of
261+
/// `Bound` start and end values. To check the entire set, use `RangeFull`
262+
/// like `set.extract_if(.., predicate)`.
263+
///
258264
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
259265
/// or the iteration short-circuits, then the remaining elements will be retained.
260266
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
@@ -269,19 +275,20 @@ impl<T, S> IndexSet<T, S> {
269275
/// use indexmap::IndexSet;
270276
///
271277
/// let mut set: IndexSet<i32> = (0..8).collect();
272-
/// let extracted: IndexSet<i32> = set.extract_if(|v| v % 2 == 0).collect();
278+
/// let extracted: IndexSet<i32> = set.extract_if(.., |v| v % 2 == 0).collect();
273279
///
274280
/// let evens = extracted.into_iter().collect::<Vec<_>>();
275281
/// let odds = set.into_iter().collect::<Vec<_>>();
276282
///
277283
/// assert_eq!(evens, vec![0, 2, 4, 6]);
278284
/// assert_eq!(odds, vec![1, 3, 5, 7]);
279285
/// ```
280-
pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, T, F>
286+
pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, T, F>
281287
where
282288
F: FnMut(&T) -> bool,
289+
R: RangeBounds<usize>,
283290
{
284-
ExtractIf::new(&mut self.map.core, pred)
291+
ExtractIf::new(&mut self.map.core, range, pred)
285292
}
286293

287294
/// Splits the collection into two at the given index.

src/set/iter.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -633,21 +633,19 @@ impl<I: fmt::Debug> fmt::Debug for UnitValue<I> {
633633
///
634634
/// This `struct` is created by [`IndexSet::extract_if()`].
635635
/// See its documentation for more.
636-
pub struct ExtractIf<'a, T, F>
637-
where
638-
F: FnMut(&T) -> bool,
639-
{
636+
pub struct ExtractIf<'a, T, F> {
640637
inner: ExtractCore<'a, T, ()>,
641638
pred: F,
642639
}
643640

644-
impl<T, F> ExtractIf<'_, T, F>
645-
where
646-
F: FnMut(&T) -> bool,
647-
{
648-
pub(super) fn new(core: &mut IndexMapCore<T, ()>, pred: F) -> ExtractIf<'_, T, F> {
641+
impl<T, F> ExtractIf<'_, T, F> {
642+
pub(super) fn new<R>(core: &mut IndexMapCore<T, ()>, range: R, pred: F) -> ExtractIf<'_, T, F>
643+
where
644+
R: RangeBounds<usize>,
645+
F: FnMut(&T) -> bool,
646+
{
649647
ExtractIf {
650-
inner: core.extract(),
648+
inner: core.extract(range),
651649
pred,
652650
}
653651
}
@@ -672,7 +670,7 @@ where
672670

673671
impl<'a, T, F> fmt::Debug for ExtractIf<'a, T, F>
674672
where
675-
F: FnMut(&T) -> bool,
673+
T: fmt::Debug,
676674
{
677675
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
678676
f.debug_struct("ExtractIf").finish_non_exhaustive()

tests/quick.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ quickcheck_limit! {
200200
let (odd, even): (Vec<_>, Vec<_>) = map.keys().copied().partition(|k| k % 2 == 1);
201201

202202
let extracted: Vec<_> = map
203-
.extract_if(|k, _| k % 2 == 1)
203+
.extract_if(.., |k, _| k % 2 == 1)
204204
.map(|(k, _)| k)
205205
.collect();
206206

@@ -222,7 +222,7 @@ quickcheck_limit! {
222222
});
223223

224224
let extracted: Vec<_> = map
225-
.extract_if(|k, _| k % 2 == 1)
225+
.extract_if(.., |k, _| k % 2 == 1)
226226
.map(|(k, _)| k)
227227
.take(limit)
228228
.collect();

0 commit comments

Comments
 (0)