Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2531050

Browse files
committedFeb 25, 2025·
Add range support to extract_if
1 parent c7f36fa commit 2531050

File tree

6 files changed

+64
-49
lines changed

6 files changed

+64
-49
lines changed
 

‎src/map.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ impl<K, V, S> IndexMap<K, V, S> {
308308
Drain::new(self.core.drain(range))
309309
}
310310

311-
/// Creates an iterator which uses a closure to determine if an element should be removed.
311+
/// Creates an iterator which uses a closure to determine if an element should be removed,
312+
/// for all elements in the given range.
312313
///
313314
/// If the closure returns true, the element is removed from the map and yielded.
314315
/// If the closure returns false, or panics, the element remains in the map and will not be
@@ -317,6 +318,11 @@ impl<K, V, S> IndexMap<K, V, S> {
317318
/// Note that `extract_if` lets you mutate every value in the filter closure, regardless of
318319
/// whether you choose to keep or remove it.
319320
///
321+
/// The range may be any type that implements [`RangeBounds<usize>`],
322+
/// including all of the `std::ops::Range*` types, or even a tuple pair of
323+
/// `Bound` start and end values. To check the entire map, use `RangeFull`
324+
/// like `map.extract_if(.., predicate)`.
325+
///
320326
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
321327
/// or the iteration short-circuits, then the remaining elements will be retained.
322328
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
@@ -331,19 +337,20 @@ impl<K, V, S> IndexMap<K, V, S> {
331337
/// use indexmap::IndexMap;
332338
///
333339
/// let mut map: IndexMap<i32, i32> = (0..8).map(|x| (x, x)).collect();
334-
/// let extracted: IndexMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect();
340+
/// let extracted: IndexMap<i32, i32> = map.extract_if(.., |k, _v| k % 2 == 0).collect();
335341
///
336342
/// let evens = extracted.keys().copied().collect::<Vec<_>>();
337343
/// let odds = map.keys().copied().collect::<Vec<_>>();
338344
///
339345
/// assert_eq!(evens, vec![0, 2, 4, 6]);
340346
/// assert_eq!(odds, vec![1, 3, 5, 7]);
341347
/// ```
342-
pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F>
348+
pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, F>
343349
where
344350
F: FnMut(&K, &mut V) -> bool,
351+
R: RangeBounds<usize>,
345352
{
346-
ExtractIf::new(&mut self.core, pred)
353+
ExtractIf::new(&mut self.core, range, pred)
347354
}
348355

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

‎src/map/core/extract.rs

+22-11
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

+9-14
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
}
@@ -818,10 +816,7 @@ where
818816
}
819817
}
820818

821-
impl<'a, K, V, F> fmt::Debug for ExtractIf<'a, K, V, F>
822-
where
823-
F: FnMut(&K, &mut V) -> bool,
824-
{
819+
impl<'a, K, V, F> fmt::Debug for ExtractIf<'a, K, V, F> {
825820
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826821
f.debug_struct("ExtractIf").finish_non_exhaustive()
827822
}

‎src/set.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,18 @@ impl<T, S> IndexSet<T, S> {
258258
Drain::new(self.map.core.drain(range))
259259
}
260260

261-
/// Creates an iterator which uses a closure to determine if a value should be removed.
261+
/// Creates an iterator which uses a closure to determine if a value should be removed,
262+
/// for all values in the given range.
262263
///
263264
/// If the closure returns true, then the value is removed and yielded.
264265
/// If the closure returns false, the value will remain in the list and will not be yielded
265266
/// by the iterator.
266267
///
268+
/// The range may be any type that implements [`RangeBounds<usize>`],
269+
/// including all of the `std::ops::Range*` types, or even a tuple pair of
270+
/// `Bound` start and end values. To check the entire set, use `RangeFull`
271+
/// like `set.extract_if(.., predicate)`.
272+
///
267273
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
268274
/// or the iteration short-circuits, then the remaining elements will be retained.
269275
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
@@ -278,19 +284,20 @@ impl<T, S> IndexSet<T, S> {
278284
/// use indexmap::IndexSet;
279285
///
280286
/// let mut set: IndexSet<i32> = (0..8).collect();
281-
/// let extracted: IndexSet<i32> = set.extract_if(|v| v % 2 == 0).collect();
287+
/// let extracted: IndexSet<i32> = set.extract_if(.., |v| v % 2 == 0).collect();
282288
///
283289
/// let evens = extracted.into_iter().collect::<Vec<_>>();
284290
/// let odds = set.into_iter().collect::<Vec<_>>();
285291
///
286292
/// assert_eq!(evens, vec![0, 2, 4, 6]);
287293
/// assert_eq!(odds, vec![1, 3, 5, 7]);
288294
/// ```
289-
pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, T, F>
295+
pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, T, F>
290296
where
291297
F: FnMut(&T) -> bool,
298+
R: RangeBounds<usize>,
292299
{
293-
ExtractIf::new(&mut self.map.core, pred)
300+
ExtractIf::new(&mut self.map.core, range, pred)
294301
}
295302

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

‎src/set/iter.rs

+9-14
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
}
@@ -670,10 +668,7 @@ where
670668
}
671669
}
672670

673-
impl<'a, T, F> fmt::Debug for ExtractIf<'a, T, F>
674-
where
675-
F: FnMut(&T) -> bool,
676-
{
671+
impl<'a, T, F> fmt::Debug for ExtractIf<'a, T, F> {
677672
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
678673
f.debug_struct("ExtractIf").finish_non_exhaustive()
679674
}

‎tests/quick.rs

+2-2
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)