-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Implement ordered/sorted iterators on BinaryHeap as per #59278 #65091
Conversation
r? @KodrAus (rust_highfive has picked a reviewer for you, use r? to override) |
impl<T: Ord> ExactSizeIterator for IntoIterOrdered<T> {} | ||
|
||
#[unstable(feature = "binary_heap_into_iter_ordered", issue = "59278")] | ||
impl<T: Ord> FusedIterator for IntoIterOrdered<T> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can add TrustedLen
for both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add TrustedLen
for the two new iterators.
I'm wondering if other existing iterators should implement TrustedLen
for consistency.
binary_heap::Iter
binary_heap::IntoIter
- Underlying
vec::IntoIter
implementsTrustedLen
- Clearly
binary_heap::IntoIter
should implementTrustedLen
, too.
- Underlying
binary_heap::Drain
- Underlying
vec::Drain
does not implementTrustedLen
. - It is not clear whether it should implement
TrustedLen
.
- Underlying
vec::Drain
- It is not clear whether it should implement
TrustedLen
.
- It is not clear whether it should implement
Please advise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should implement TrustedLen
if and only if it would be sound to do so. However, I think it's best to make a follow-up issue and treat with them there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I go with the conservative way. (Implement it for the two new ordered iterators only)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TrustedLen
was added.
|
||
#[unstable(feature = "binary_heap_drain_ordered", issue = "59278")] | ||
impl<'a, T> Drop for DrainOrdered<'a, T> { | ||
/// Removes heap elements in arbitrary order for efficiency. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should clear them in order, it seems surprising otherwise. The common theme of the .drain()
iterators are that they have sideeffectful drops, so it wouldn't be out of place? It can be guarded by a needs-drop check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That has complexity side-effects I'd consider surprising, though -- I'd hope that .drain().take(k)
is O(k lg n)
, but if they have to drop in order, it'd be O(n lg n)
.
The BinaryHeap
itself drops in arbitrary order, so I don't think it's crazy for drain
to also do so. If someone really wants to drop in sorted order, then can always .for_each(drop)
.
EDIT: Or worse, if the element is !needs_drop
, the drop being O(n ln n)
instead of O(1)
seems particularly unfortunate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In one way it seems to me that .drain_ordered()
already is an intention of taking all the elements and dropping them in order. But I think your expectation there is fair.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like expensive operation on implicit call (such as drop()
) is not a good thing in the Rust language.
Therefore, I kept the behavior requiring user to explicitly consume elements to perform expensive O(n ln n)
operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now .drain_sorted()
is changed to always remove elements in heap order as @bluss has suggested.
I suppose this simplifies usage a lot. The previous behavior was hard to explain in the example. "hard to explain" feels like a possible sign of "bad API" and I changed it to the more straightforward implementation.
Thanks for comments! I removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR @sekineh!
I've just left a few comments.
/// assert_eq!(heap.into_iter_ordered().take(2).collect::<Vec<_>>(), vec![5, 4]); | ||
/// ``` | ||
#[unstable(feature = "binary_heap_into_iter_ordered", issue = "59278")] | ||
pub fn into_iter_ordered(self) -> IntoIterOrdered<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the naming convention we have elsewhere would call this into_iter_sorted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that sorted
is more common in std.
done in 4da6540
/// ``` | ||
#[inline] | ||
#[unstable(feature = "binary_heap_drain_ordered", issue = "59278")] | ||
pub fn drain_ordered(&mut self) -> DrainOrdered<'_, T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drain_sorted
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 4da6540
/// Note: | ||
/// * Unlike other `.drain()` methods, this method removes elements *lazily*. | ||
/// In order to remove elements in heap order, you need to retrieve elements explicitly. | ||
/// * The remained elements are removed on drop (out of scope), in arbitrary order for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could just say here The remaining elements are removed on drop in arbitrary order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done in 38c77ae
/// The retrieved elements will be removed from the original heap. | ||
/// | ||
/// Note: | ||
/// * Unlike other `.drain()` methods, this method removes elements *lazily*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having drain_sorted
behave differently than other drain
methods with respect to optimistically clearing capacity seems unfortunate. Combined with the inconsistent order of any remaining elements in drop
it's looking like a method that isn't going to behave the way you expect unless you hold it a specific way.
I feel like we'll need a few examples that are at least close to some real-world usage to get a sense of whether these trade-offs really are reasonable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 30e8f65, I made .drain_sorted()
to always remove elements in heap order.
This simplified doc example a lot.
☔ The latest upstream changes (presumably #65251) made this pull request unmergeable. Please resolve the merge conflicts. |
I’ll rebase and work it in the next week. |
(I used git in some wrong way when I tried to do git rebase. trying to fix now) |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
bef3fb1
to
38c77ae
Compare
* `.drain_sorted()` doc change suggested by @KodrAus
38c77ae
to
6b88073
Compare
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
Now I think it’s ready to be reviewed again. |
📌 Commit 95442ae has been approved by |
Implement ordered/sorted iterators on BinaryHeap as per #59278 I've implemented the ordered version of iterator on BinaryHeap as per #59278. # Added methods: * `.into_iter_sorted()` * like `.into_iter()`; but returns elements in heap order * `.drain_sorted()` * like `.drain()`; but returns elements in heap order * It's a bit _lazy_; elements are removed on drop. (Edit: it’s similar to vec::Drain) For `DrainSorted` struct, I implemented `Drop` trait following @scottmcm 's [suggestion](#59278 (comment)) # ~TODO~ DONE * ~I think I need to add more tests other than doctest.~ # **Notes:** * we renamed `_ordered` to `_sorted`, because the latter is more common in rust libs. (as suggested by @KodrAus )
☀️ Test successful - checks-azure |
I've implemented the ordered version of iterator on BinaryHeap as per #59278.
Added methods:
.into_iter_sorted()
.into_iter()
; but returns elements in heap order.drain_sorted()
.drain()
; but returns elements in heap orderFor
DrainSorted
struct, I implementedDrop
trait following @scottmcm 's suggestionTODODONEI think I need to add more tests other than doctest.Notes:
_ordered
to_sorted
, because the latter is more common in rust libs. (as suggested by @KodrAus )