Skip to content

Conversation

@RalfJung
Copy link
Member

Also, mention the .for_each(drop) pattern in the documentation. One can't always use retain with a negated predicate as that does not have a range argument.

r? @the8472

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Nov 16, 2025
Copy link
Member

@the8472 the8472 left a comment

Choose a reason for hiding this comment

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

It'd be nicer if we had map.range_mut(..).discard() and similar range operations. But until then extract_if is the best we offer... ok.

View changes since this review

/// assert_eq!(high.keys().copied().collect::<Vec<_>>(), [4, 5, 6, 7]);
/// ```
#[stable(feature = "btree_extract_if", since = "1.91.0")]
#[must_use = "use `extract_if().for_each(drop)` to remove all extracted elements"]
Copy link
Member

Choose a reason for hiding this comment

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

The wording we have on most iterators is simply

#[must_use = "iterators are lazy and do nothing unless consumed"]

It's possible that the user meant to use the iterator results but forgot to somehow.

for_each(drop) is only recommended by an unused collect()

Copy link
Member Author

Choose a reason for hiding this comment

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

The drain iterator is also lazy and still does something when not consumed... so if the user knows drain, which is likely, then just talking about the laziness of iterators in general is not enough for this specific case IMO. I think it makes sense to tell people how to use this as a drain-like API.

Copy link
Member

Choose a reason for hiding this comment

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

Drain is an exception and we deliberately renamed it from DrainFilter to ExtractIf to avoid that association.

I guess you could add the general "iterators are lazy" statement and follow up with a "use retain or for_each(drop)" after that.

Generally we don't want to encourage for_each(drop) too much because it can indicate misuse such as iter.inspect(side_effect).for_each(drop) instead of iter.for_each(side_effect). But in this case it's a legitimate use, due to lack of better alternatives.

When we have a better alternative we can remove the for_each part of the hint.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have updated the wording:

    #[must_use = "this iterator is lazy and does nothing unless consumed; \
        use `retain` or `extract_if().for_each(drop)` to remove and discard elements"]

I went for "this iterator" to make it more clear that this applies to extract_if despite potentially conflicting expectations raised by drain. (I also considered something like "the iterator returned by extract_if" but found that too wordy.)

@RalfJung RalfJung force-pushed the extract_if branch 3 times, most recently from 59d5167 to 0604669 Compare November 16, 2025 13:45
/// ```
#[stable(feature = "btree_extract_if", since = "1.91.0")]
#[must_use = "this iterator is lazy and does nothing unless consumed; \
use `retain` or `extract_if().for_each(drop)` to remove and discard elements"]
Copy link
Member Author

Choose a reason for hiding this comment

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

Should we remind people that retain needs the predicate to be negated?

Alternatively we could only mention the for_each variant here. We do still mention retain in the docs for people that have a closer look.

Copy link
Member

Choose a reason for hiding this comment

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

must_use hints seem to be terse in general. I assume this is to not bloat diagnostics, so let's keep it this way.

Comment on lines 691 to 693
#[must_use = "this iterator is lazy and does nothing unless consumed; \
use `extract_if().for_each(drop)` to remove and discard elements"]
pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F>
Copy link
Member

Choose a reason for hiding this comment

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

The functions without range arguments should only point to retain.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah right, I forgot the Hash* collections have those

Copy link
Member Author

Choose a reason for hiding this comment

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

FWIW personally I would probably still use extract_if in situations where the negation enforced by retain is awkward... in most of these cases, I want to characterize the elements to be removed, and then extract_if(...).for_each(drop) might beat retain(with-negation).

@hanna-kruppe
Copy link
Contributor

The ExtractIf iterator types are already marked as #[must_use] (except for hash_set::ExtractIf, which seems like a mistake). So how about leaving the methods as-is and moving the more specific messages to the respective return type's #[must_use]?

@RalfJung
Copy link
Member Author

The ExtractIf iterator types are already marked as #[must_use] (except for hash_set::ExtractIf, which seems like a mistake). So how about leaving the methods as-is and moving the more specific messages to the respective return type's #[must_use]?

Good point! I did that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants