Skip to content

Implement extract_if for BinaryHeap#154317

Open
Nokel81 wants to merge 4 commits intorust-lang:mainfrom
Nokel81:extract-if-binary-heap
Open

Implement extract_if for BinaryHeap#154317
Nokel81 wants to merge 4 commits intorust-lang:mainfrom
Nokel81:extract-if-binary-heap

Conversation

@Nokel81
Copy link
Copy Markdown
Contributor

@Nokel81 Nokel81 commented Mar 24, 2026

View all comments

  • Implement using the sort and rebuild on drop method as maintaining the heap invariant during each extraction would cost more on average

resolves #42849

I have seen #104210 and I took away from reading the comments there that this might be accepted if it does the sort-in-place and then rebuild on drop semantics

@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 Mar 24, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 24, 2026

r? @Mark-Simulacrum

rustbot has assigned @Mark-Simulacrum.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: libs
  • libs expanded to 7 candidates

@rust-log-analyzer

This comment has been minimized.

@Nokel81 Nokel81 force-pushed the extract-if-binary-heap branch 2 times, most recently from f1ca823 to 7fcbbfb Compare March 25, 2026 15:53
@rust-log-analyzer

This comment has been minimized.

@Nokel81 Nokel81 force-pushed the extract-if-binary-heap branch from 7fcbbfb to 96ab588 Compare March 26, 2026 14:09
@rust-log-analyzer

This comment has been minimized.

@Nokel81 Nokel81 force-pushed the extract-if-binary-heap branch from 96ab588 to e2c7e95 Compare March 26, 2026 14:19
@rust-log-analyzer

This comment has been minimized.

- Implement using the sort and rebuild on drop method as maintaining the heap invariant during each extraction would cost more on average
@Nokel81 Nokel81 force-pushed the extract-if-binary-heap branch from e2c7e95 to 69031cf Compare March 27, 2026 14:32
Copy link
Copy Markdown
Member

@Mark-Simulacrum Mark-Simulacrum left a comment

Choose a reason for hiding this comment

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

Nominating for libs-api -- I'd recommend you wait until they discuss before continuing with changes. I think the two topics are (a) do we support extract_if over a subset of the elements, and (2) do we want this at all, given that it can be implemented just as efficiently and fairly easily for the general case on stable today?

View changes since this review

unsafe {
self.heap.data.set_len(self.old_len - self.del);
}
self.heap.rebuild();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looking through this implementation, it seems to me that we end up replicating Vec::extract_if pretty closely. Maybe we can use it directly, possibly refactoring the impl to allow using it on a passed-in &mut Vec<T, A> for each call (essentially separating the state and the Vec being iterated)?

Alternatively, it's possible that the right pattern is to tell users that the right pattern for extract_if is:

let vec = BinaryHeap::into_sorted_vec(mem::take(heap)); // take only needed if you have only &mut.
vec.extract_if(...);
*heap = BinaryHeap::from(vec);

AFAICT, that is equally efficient to the implementation here, and we can't do better since we give &mut T access to the elements: we have to assume all elements were changed by the pass over them, so we can't rebuild more efficiently than re-sifting the whole heap. (Even though in the common case removals happened but probably no changes to the elements occurred).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I had considered trying to directly reuse the Vec::extract_if impl directly, but I was unable to figure out how to allocate an empty Vec<T> in an arbitrary allocator given only a reference to the allocator

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have found a way to reuse the Vec::extract_if implementation, PTAL

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 27, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 27, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Mar 27, 2026
@Mark-Simulacrum Mark-Simulacrum added the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Mar 27, 2026
@joshtriplett
Copy link
Copy Markdown
Member

We talked about this in today's @rust-lang/libs-api meeting.

  1. We do want this.
  2. This shouldn't take a range or operate on subsets.
  3. This should not sort first; it should iterate in arbitrary order.

@joshtriplett joshtriplett removed the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Mar 31, 2026
@Nokel81
Copy link
Copy Markdown
Contributor Author

Nokel81 commented Apr 1, 2026

@joshtriplett Thanks for the reply! I'll update the implementation to not sort on creation but still to rebuild on drop. What about providing &mut access to each element?

@Amanieu
Copy link
Copy Markdown
Member

Amanieu commented Apr 1, 2026

Providing a &mut T is fine, this is what we do for Vec as well. In fact the implementation should be a wrapper around Vec::extract_if to avoid code duplication.

@scottmcm
Copy link
Copy Markdown
Member

scottmcm commented Apr 1, 2026

Is this really worth having over just using into_vec, doing the extract, then rebuilding the heap? If this could do something smart then sure, but it sounds like it's not.

(At most a take_vec method, though you can just take(&mut heap).into_vec() so that's not really needed.)

@Nokel81
Copy link
Copy Markdown
Contributor Author

Nokel81 commented Apr 2, 2026

Currently we cannot take the binary heap since vectors using custom allocators do not implement Default (I plan on opening a PR for that soon)

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

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

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. 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.

Add drain_filter for BinaryHeap

7 participants