-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Tracking issue for map_first_last: first/last methods on BTreeSet and BTreeMap #62924
Comments
Here are what the other collections call similar methods:
|
#59947 is related -- |
#64820 is also related - BTreeSet is_subset, intersection and difference now always do this double work (they actually want both min and max at the same time, but requesting both I naively thought the redundant data (the other end and iterator length) and code would all get optimized away from an expression like |
According to a raw benchmark, the specialized functions are about 3 times faster than Details: benchmark code in liballoc/benches/btree/map.rs:
Results using the new functions:
Results using .iter().next() / .iter().next_back():
Results using .range(..).next() / .range(..).next_back():
|
proposal for BTreeMap/Set min/max, #62924 - Which pair of names: #62924 lists the existing possibilities min/max, first/last, (EDIT) front/back, peek(/peek_back?). Iterators have next/next_back or next/last. I'm slightly in favour of first/last because min/max might suggest they search over the entire map, and front/back pretends they are only about position. - Return key only instead of pair like iterator does? - If not, then keep the _key_value suffix? ~~Also provide variant with mutable value? But there is no such variant for get_key_value.~~ - Look for and upgrade more usages of `.iter().next()` and such in the libraries? I only upgraded the ones I contributed myself, all very recently.
I vote for min/max rather than first/last terminology. Rationale: I’ve recently had a bad bug when I used Rust’s BinaryHeap and assumed that pop yields a minimal item. If the API had used max in the method name, I wouldn’t have made the bug. This case is similar: first is ambiguous, min is precise (and shorter ^^). |
I'm slightly in favour of min/max at the moment, having faced reverse iteration in (other) code with first/last names. It's a close call; min/max is ambiguous about performance, because many min/max functions out there are in fact linear searches. PS then again, that argument makes |
Correctness is more important than performance, and the performance ambiguity is an a safe direction (operation is faster than it might seem) |
Can we also get |
I didn't add those back then because I thought they were intended to be used through the entry API. But using that in practice seems more complicated than I would want. There's no entry concept in BTreeSet, so I wouldn't start mixing the naming scheme though. First/last or min/max (or something else) all the way. I don't remember any discussion on whether it should be |
In the rust code base, there's only one use of this new API: By the way, that use would be slightly simpler if we had a |
And then there's the "_key_value" suffix. In a different line of development, But That means |
randomly come across this. pop_first and pop_last are more appropriate than min/max since this is an iteration and an iteration comes with the first and ends with the last. |
@pipehappy1, I don't understand what you mean. Which "this" is an iteration? Of course first and last correspond to the various forms of forward iteration on maps, but that doesn't imply first/last is any clearer than min/max. I think both naming schemes are reasonable. Min/max when the caller can have a good notion of how keys are ordered, and really wants the minimum or maximum. As it stands, almost all members have an Therefore, the first/last case is also reasonable, when the caller has no notion of key order and just wants to follow whatever order the container chooses, regardless of whether the container is a map or a vector (of pairs). But then it should not have the |
@ssomers That's quite clear and an insight. Both naming schemes get their reason. Thx |
Point made in internals: the fact that first/last_entry are not constant time is the real problem. The "twice" in the title of this issue is just peanuts, the "required amount of computation" matters more. PS But if it's not feasible to reduce the required amount of computation, the "twice" are still tasty peanuts. |
BTreeMap first last proposal tweaks Clean-up and following up on a request in rust-lang#62924. Trying the reviewer of the original code rust-lang#65637... r? @scottmcm
BTreeMap first last proposal tweaks Clean-up and following up on a request in rust-lang#62924. Trying the reviewer of the original code rust-lang#65637... r? @scottmcm
Is there any plan to stablize thie feature ? Since pop_first/pop_last is really useful. |
minor example error for BTreeSet::last(). Shouldn't assert_eq!(map.first(), None); be assert_eq!(map.last(), None); ? |
@BurntSushi @dtolnay @joshtriplett is there any potential next step here? This seems like an unusually long wait for FCP. |
As the FCP is close to starting (only one more approval needed), I've opened a PR to stabilize the |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
I've been away for the past few weeks and haven't had much time to check up on Rust-related things. I've checked my box, this API looks fine to me. |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
…m-ou-se Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes rust-lang#62924 ~~Blocked on the [FCP](rust-lang#62924 (comment)) finishing.~~ Edit: It finished!
…m-ou-se Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes rust-lang#62924 ~~Blocked on the [FCP](rust-lang#62924 (comment)) finishing.~~ Edit: It finished!
Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes #62924 ~~Blocked on the [FCP](rust-lang/rust#62924 (comment)) finishing.~~ Edit: It finished!
Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes #62924 ~~Blocked on the [FCP](rust-lang/rust#62924 (comment)) finishing.~~ Edit: It finished!
Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes #62924 ~~Blocked on the [FCP](rust-lang/rust#62924 (comment)) finishing.~~ Edit: It finished!
Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes #62924 ~~Blocked on the [FCP](rust-lang/rust#62924 (comment)) finishing.~~ Edit: It finished!
Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes #62924 ~~Blocked on the [FCP](rust-lang/rust#62924 (comment)) finishing.~~ Edit: It finished!
Stabilize map_first_last Stabilizes the following functions: ```Rust impl<T> BTreeSet<T> { pub fn first(&self) -> Option<&T> where T: Ord; pub fn last(&self) -> Option<&T> where T: Ord; pub fn pop_first(&mut self) -> Option<T> where T: Ord; pub fn pop_last(&mut self) -> Option<T> where T: Ord; } impl<K, V> BTreeMap<K, V> { pub fn first_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn last_key_value(&self) -> Option<(&K, &V)> where K: Ord; pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V>> where K: Ord; pub fn pop_first(&mut self) -> Option<(K, V)> where K: Ord; pub fn pop_last(&mut self) -> Option<(K, V)> where K: Ord; } ``` Closes #62924 ~~Blocked on the [FCP](rust-lang/rust#62924 (comment)) finishing.~~ Edit: It finished!
Feature gate:
#![feature(map_first_last)]
Public API
Steps / History
Unresolved Questions
Original issue title: Finding minimum/maximum element in BTreeMap does twice the required amount of computation
Original issue text:
The standard way of finding the minimum element in BTreeMap is
tree_map.iter().next()
The
iter()
function creates abtree_map::Iter
, which finds both the minimum and maximum element to create its internal instance ofbtree_map::Range
.This is unnecessary, and a
BTreeMap::get_min
or similar function could be provided, which would just internally call thefirst_leaf_edge
private function, without needing to calllast_leaf_edge
.Ditto for finding the maximum element.
The text was updated successfully, but these errors were encountered: