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 7329f51

Browse files
committedNov 20, 2024·
Add range support to extract_if
1 parent 49ed21a commit 7329f51

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
@@ -307,7 +307,8 @@ impl<K, V, S> IndexMap<K, V, S> {
307307
Drain::new(self.core.drain(range))
308308
}
309309

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

348355
/// 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
@@ -777,21 +777,19 @@ where
777777
///
778778
/// This `struct` is created by [`IndexMap::extract_if()`].
779779
/// See its documentation for more.
780-
pub struct ExtractIf<'a, K, V, F>
781-
where
782-
F: FnMut(&K, &mut V) -> bool,
783-
{
780+
pub struct ExtractIf<'a, K, V, F> {
784781
inner: ExtractCore<'a, K, V>,
785782
pred: F,
786783
}
787784

788-
impl<K, V, F> ExtractIf<'_, K, V, F>
789-
where
790-
F: FnMut(&K, &mut V) -> bool,
791-
{
792-
pub(super) fn new(core: &mut IndexMapCore<K, V>, pred: F) -> ExtractIf<'_, K, V, F> {
785+
impl<K, V, F> ExtractIf<'_, K, V, F> {
786+
pub(super) fn new<R>(core: &mut IndexMapCore<K, V>, range: R, pred: F) -> ExtractIf<'_, K, V, F>
787+
where
788+
R: RangeBounds<usize>,
789+
F: FnMut(&K, &mut V) -> bool,
790+
{
793791
ExtractIf {
794-
inner: core.extract(),
792+
inner: core.extract(range),
795793
pred,
796794
}
797795
}
@@ -817,10 +815,7 @@ where
817815
}
818816
}
819817

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

‎src/set.rs

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

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

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

‎src/set/iter.rs

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

643-
impl<T, F> ExtractIf<'_, T, F>
644-
where
645-
F: FnMut(&T) -> bool,
646-
{
647-
pub(super) fn new(core: &mut IndexMapCore<T, ()>, pred: F) -> ExtractIf<'_, T, F> {
640+
impl<T, F> ExtractIf<'_, T, F> {
641+
pub(super) fn new<R>(core: &mut IndexMapCore<T, ()>, range: R, pred: F) -> ExtractIf<'_, T, F>
642+
where
643+
R: RangeBounds<usize>,
644+
F: FnMut(&T) -> bool,
645+
{
648646
ExtractIf {
649-
inner: core.extract(),
647+
inner: core.extract(range),
650648
pred,
651649
}
652650
}
@@ -669,10 +667,7 @@ where
669667
}
670668
}
671669

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

‎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)
Please sign in to comment.