Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a range argument to vec.extract_if #133265

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1558,18 +1558,18 @@ impl DiagCtxtInner {
debug!(?diagnostic);
debug!(?self.emitted_diagnostics);

let already_emitted_sub = |sub: &mut Subdiag| {
let not_yet_emitted = |sub: &mut Subdiag| {
debug!(?sub);
if sub.level != OnceNote && sub.level != OnceHelp {
return false;
return true;
}
let mut hasher = StableHasher::new();
sub.hash(&mut hasher);
let diagnostic_hash = hasher.finish();
debug!(?diagnostic_hash);
!self.emitted_diagnostics.insert(diagnostic_hash)
self.emitted_diagnostics.insert(diagnostic_hash)
};
diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
diagnostic.children.retain_mut(not_yet_emitted);
if already_emitted {
let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`";
diagnostic.sub(Note, msg, MultiSpan::new());
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_lint/src/non_ascii_idents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ impl EarlyLintPass for NonAsciiIdents {
(IdentifierType::Not_NFKC, "Not_NFKC"),
] {
let codepoints: Vec<_> =
chars.extract_if(|(_, ty)| *ty == Some(id_ty)).collect();
chars.extract_if(.., |(_, ty)| *ty == Some(id_ty)).collect();
if codepoints.is_empty() {
continue;
}
Expand All @@ -217,7 +217,7 @@ impl EarlyLintPass for NonAsciiIdents {
}

let remaining = chars
.extract_if(|(c, _)| !GeneralSecurityProfile::identifier_allowed(*c))
.extract_if(.., |(c, _)| !GeneralSecurityProfile::identifier_allowed(*c))
.collect::<Vec<_>>();
if !remaining.is_empty() {
cx.emit_span_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ impl<'tcx> Collector<'tcx> {
// can move them to the end of the list below.
let mut existing = self
.libs
.extract_if(|lib| {
.extract_if(.., |lib| {
if lib.name.as_str() == passed_lib.name {
// FIXME: This whole logic is questionable, whether modifiers are
// involved or not, library reordering and kind overriding without
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ pub fn suggest_constraining_type_params<'a>(
let Some(param) = param else { return false };

{
let mut sized_constraints = constraints.extract_if(|(_, def_id)| {
let mut sized_constraints = constraints.extract_if(.., |(_, def_id)| {
def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))
});
if let Some((_, def_id)) = sized_constraints.next() {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2805,11 +2805,11 @@ fn show_candidates(
path_strings.sort_by(|a, b| a.0.cmp(&b.0));
path_strings.dedup_by(|a, b| a.0 == b.0);
let core_path_strings =
path_strings.extract_if(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::<Vec<_>>();
let std_path_strings =
path_strings.extract_if(|p| p.0.starts_with("std::")).collect::<Vec<_>>();
path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::<Vec<_>>();
let foreign_crate_path_strings =
path_strings.extract_if(|p| !p.0.starts_with("crate::")).collect::<Vec<_>>();
path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::<Vec<_>>();

// We list the `crate` local paths first.
// Then we list the `std`/`core` paths.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
// Try to filter out intrinsics candidates, as long as we have
// some other candidates to suggest.
let intrinsic_candidates: Vec<_> = candidates
.extract_if(|sugg| {
.extract_if(.., |sugg| {
let path = path_names_to_string(&sugg.path);
path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")
})
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ pub fn normalize_param_env_or_error<'tcx>(
// This works fairly well because trait matching does not actually care about param-env
// TypeOutlives predicates - these are normally used by regionck.
let outlives_predicates: Vec<_> = predicates
.extract_if(|predicate| {
.extract_if(.., |predicate| {
matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..))
})
.collect();
Expand Down
9 changes: 2 additions & 7 deletions library/alloc/src/collections/linked_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1939,9 +1939,7 @@ pub struct ExtractIf<
T: 'a,
F: 'a,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
> where
F: FnMut(&mut T) -> bool,
{
> {
list: &'a mut LinkedList<T, A>,
it: Option<NonNull<Node<T>>>,
pred: F,
Expand Down Expand Up @@ -1979,10 +1977,7 @@ where
}

#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
impl<T: fmt::Debug, F> fmt::Debug for ExtractIf<'_, T, F>
where
F: FnMut(&mut T) -> bool,
{
impl<T: fmt::Debug, F> fmt::Debug for ExtractIf<'_, T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("ExtractIf").field(&self.list).finish()
}
Expand Down
50 changes: 25 additions & 25 deletions library/alloc/src/vec/extract_if.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::ops::{Range, RangeBounds};
use core::{ptr, slice};

use super::Vec;
Expand All @@ -14,7 +15,7 @@ use crate::alloc::{Allocator, Global};
/// #![feature(extract_if)]
///
/// let mut v = vec![0, 1, 2];
/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(|x| *x % 2 == 0);
/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0);
/// ```
#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
#[derive(Debug)]
Expand All @@ -24,24 +25,32 @@ pub struct ExtractIf<
T,
F,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
> where
F: FnMut(&mut T) -> bool,
{
pub(super) vec: &'a mut Vec<T, A>,
> {
vec: &'a mut Vec<T, A>,
/// The index of the item that will be inspected by the next call to `next`.
pub(super) idx: usize,
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.
pub(super) del: usize,
del: usize,
/// The original length of `vec` prior to draining.
pub(super) old_len: usize,
old_len: usize,
/// The filter test predicate.
pub(super) pred: F,
pred: F,
}

impl<T, F, A: Allocator> ExtractIf<'_, T, F, A>
where
F: FnMut(&mut T) -> bool,
{
impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> {
pub(super) fn new<R: RangeBounds<usize>>(vec: &'a mut Vec<T, A>, pred: F, range: R) -> Self {
let old_len = vec.len();
let Range { start, end } = slice::range(range, ..old_len);

// Guard against the vec getting leaked (leak amplification)
unsafe {
vec.set_len(0);
}
ExtractIf { vec, idx: start, del: 0, end, old_len, pred }
}

/// Returns a reference to the underlying allocator.
#[unstable(feature = "allocator_api", issue = "32838")]
#[inline]
Expand All @@ -59,7 +68,7 @@ where

fn next(&mut self) -> Option<T> {
unsafe {
while self.idx < self.old_len {
while self.idx < self.end {
let i = self.idx;
let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
let drained = (self.pred)(&mut v[i]);
Expand All @@ -82,24 +91,15 @@ where
}

fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.old_len - self.idx))
(0, Some(self.end - self.idx))
}
}

#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
impl<T, F, A: Allocator> Drop for ExtractIf<'_, T, F, A>
where
F: FnMut(&mut T) -> bool,
{
impl<T, F, A: Allocator> Drop for ExtractIf<'_, T, F, A> {
fn drop(&mut self) {
unsafe {
the8472 marked this conversation as resolved.
Show resolved Hide resolved
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);
Expand Down
43 changes: 28 additions & 15 deletions library/alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3610,12 +3610,15 @@ impl<T, A: Allocator> Vec<T, A> {
Splice { drain: self.drain(range), replace_with: replace_with.into_iter() }
}

/// Creates an iterator which uses a closure to determine if an element should be removed.
/// Creates an iterator which uses a closure to determine if element in the range should be removed.
///
/// If the closure returns true, then the element is removed and yielded.
/// If the closure returns false, the element will remain in the vector and will not be yielded
/// by the iterator.
///
/// Only elements that fall in the provided range are considered for extraction, but any elements
/// after the range will still have to be moved if any element has been extracted.
///
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
/// or the iteration short-circuits, then the remaining elements will be retained.
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
Expand All @@ -3625,10 +3628,12 @@ impl<T, A: Allocator> Vec<T, A> {
/// Using this method is equivalent to the following code:
///
/// ```
/// # use std::cmp::min;
/// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 };
/// # let mut vec = vec![1, 2, 3, 4, 5, 6];
/// let mut i = 0;
/// while i < vec.len() {
/// # let range = 1..4;
/// let mut i = range.start;
/// while i < min(vec.len(), range.end) {
/// if some_predicate(&mut vec[i]) {
/// let val = vec.remove(i);
/// // your code here
Expand All @@ -3643,8 +3648,12 @@ impl<T, A: Allocator> Vec<T, A> {
/// But `extract_if` is easier to use. `extract_if` is also more efficient,
/// because it can backshift the elements of the array in bulk.
///
/// Note that `extract_if` also lets you mutate every element in the filter closure,
/// regardless of whether you choose to keep or remove it.
/// Note that `extract_if` also lets you mutate the elements passed to the filter closure,
/// regardless of whether you choose to keep or remove them.
///
/// # Panics
///
/// If `range` is out of bounds.
///
/// # Examples
///
Expand All @@ -3654,25 +3663,29 @@ impl<T, A: Allocator> Vec<T, A> {
/// #![feature(extract_if)]
/// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15];
///
/// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::<Vec<_>>();
/// let evens = numbers.extract_if(.., |x| *x % 2 == 0).collect::<Vec<_>>();
/// let odds = numbers;
///
/// assert_eq!(evens, vec![2, 4, 6, 8, 14]);
/// assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]);
/// ```
///
/// Using the range argument to only process a part of the vector:
///
/// ```
/// #![feature(extract_if)]
/// let mut items = vec![0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2];
/// let ones = items.extract_if(7.., |x| *x == 1).collect::<Vec<_>>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change one of the first few 0s to 1 to indicate they don't get extracted (out of range)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm, I wanted to show the "I have a prefix that I know doesn't contain those things, so I can skip them" use.

/// assert_eq!(items, vec![0, 0, 0, 0, 0, 0, 0, 2, 2, 2]);
/// assert_eq!(ones.len(), 3);
/// ```
#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
pub fn extract_if<F>(&mut self, filter: F) -> ExtractIf<'_, T, F, A>
pub fn extract_if<F, R>(&mut self, range: R, filter: F) -> ExtractIf<'_, T, F, A>
where
F: FnMut(&mut T) -> bool,
R: RangeBounds<usize>,
{
let old_len = self.len();

// Guard against us getting leaked (leak amplification)
unsafe {
self.set_len(0);
}

ExtractIf { vec: self, idx: 0, del: 0, old_len, pred: filter }
ExtractIf::new(self, filter, range)
}
}

Expand Down
Loading
Loading