-
Notifications
You must be signed in to change notification settings - Fork 20
pop_if
or Entry
/Peek
-like API for Vec
and VecDeque
#208
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
Comments
I think Also I'd note that your Entry API here seems to work with Deref, but the map ones do not. |
Thank you, fixed! |
Related: rust-lang/rfcs#3082. |
Other than that, 👍 to both |
Would it be best to have the impl<T> Vec<T> {
pub fn pop_if<F>(&mut self, predicate: F) -> Option<T>
where F: FnOnce(&mut T) -> bool;
// ^^^
} |
@pitaj Yes, that's what I was proposing in #208 (comment) ; possibly our messages raced. :) |
Oh they didn't race, I just totally skipped over that portion of your comment. Woops |
Thank you both for your feedback, I've updated, the proposal.
Should I remove the What would the next steps be to move this forward? Thanks! |
Feel free to open a tracking issue and open a PR to rust-lang/rust to add And the same thing for |
Proposal
Problem statement
With the methods available on
Vec
andVecDeque
, it is not possible to inspect a value at a given index (or at the front or back of the collection) and then modify or remove the value without at least oneunwrap
. An API similar toHashMap::entry
orBinaryHeap::peek_mut
would enable users to access an element and modify or remove it if it exists, or insert it if not.Motivation, use-cases
Using the methods currently available on a
Vec
, here is how one would remove the last element if it fulfills a certain condition:In the body of the
if
statement, we pop the value that we just inspected, but are forced to callunwrap
even though there will always be a value present, since we just checked that the value fulfilled a condition. Having to callunwrap
orexpect
on what will always be aSome
is inelegant, and may require users to disable a lint rule.More fundamentally, this pattern requires accessing the collection multiple times, when it could be seen as a single operation. Another use-case that has the same issue is modifying a value if it exists or inserting a new one if the collection is empty:
There's nothing problematic about this snippet, but, like the first example, it could also benefit from an abstraction over the last value in a collection.
Solution sketches
Vec::pop_if
The initial, and simplest, suggestion is a
Vec::pop_if
method, which would remove the last element in the vec if there is at least one element and the element fulfilled a given predicate:This solution fulfills the requirements of the first example. We can now conditionally remove an item with just one method call and no unwrapping:
A
VecDeque
would have similarpop_back_if
andpop_front_if
methods.remove_if
andswap_remove_if
could also be analogues toVec::remove
andVec::swap_remove
, respectively.Peek
A slightly more complex but more powerful solution would be something simlar to
BinaryHeap::peek_mut
:The first example above would become this:
It would not have an effect on the
modify_or_insert
example.We just have one method call on the collection itself, and no more unwrapping. We would also be able to perform more complex logic on the value without stuffing it into a closure.
This API also has a notable difference from the fn-based
pop-if
API: it separates the steps of checking whether the collection has an element (by callingpeek
) from accesing the element itself (by invokingDeref
, callingpop
, etc). In this way, it's more generic and flexible thanpop_if
, allowing more complex access patterns.VecDeque
would have similarpeek_back
andpeek_front
methods, though whether those would need separate structs depends on how it's implemented.Entry
Another possible solution is an API analagous to
HashMap::entry
. Users request a specific index, and get an enum indicating whether that index is in-bounds for the collection or if it is the first empty index. If the index is valid, then users can edit the value. Otherwise, they can insert a value at the given index or into an empty collection.Using this API, the given example would look like this (and like with
Peek
, would be simplified by if-let chains):However, it would also enable more complex usage:
This
Entry
API has the same advantage over thepop_if
method thatPeek
does, while also allowing users to access an arbitrary index in the collection rather than just the first or last elements. It also enables the use of a more fluent API for themodify_or_insert
example:However, this API can be significantly more complex to implement and use, and it's unclear whether arbitrary index access in this way adds significant value. One possibility is to restrict this API to only access the front or back of a collection (i.e., remove
fn entry(index) -> Option<_>
), making this very similar to thePeek
API but with functionality to modify and replace values rather than just remove them.Finally, it is worth noting that
Peek
andEntry
are not mutually exclusive to thepop_if
method.pop_if
fulfills a rather specific use case, whilePeek
/Entry
are abstractions over the same access pattern thatpop_if
would follow.Links and related work
Zulip topic with the initial discussion
std::collections::hash_map::Entry
std::collections::binary_heap::PeekMut
The text was updated successfully, but these errors were encountered: