-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Consider adding an Iterator-like Entry API to give sugary methods #353
Comments
Yeah providing convenience methods on Entry is something that's come up a few times. It was part of the RFC discussion, where I vaguely recall it was deemed "too much". Possibly just a pre-1.0 thing. I actually implemented a mock However I don't think this merits a trait. We're avoiding adding traits in libcollections at the moment, and if it has a single method, it's not a huge deal to maintain concretely? Dunno if this work should wait for post-1.0 or not. |
The point of a trait is to get the implementations automatically, for all entries; just like all iterators automatically get E.g. trait Entry<'a, K, V> {
/// Returns Some if there's already data here.
fn into_mut(self) -> Result<&'a mut V, Self>;
fn set(self, value: V) -> &'a mut V;
fn find_or_insert(self, value: V) -> &'a mut V {
match self.into_mut() {
Ok(x) => x,
Err(e) => e.set(value)
}
}
} Now that I think about it, it's not obvious to me how many other convenience methods there are that make sense (as you say); that said, (Also, side note: I just noticed, there's no equivalent to |
See here for some of my notes on what's up with the current treatment of keys in maps. At very least in the rust repo, the key functionality was never actually used with any of the Also having to precompute the value when using Ah, I misunderstood precisely what you're proposing. You want to introduce three traits, one for Entry, one for VacantEntry, and one for OccupiedEntry. The latter two would be concretely impled, and the former would be automatically impled. In fact, you can do one better and only have VacantEntry and OccupiedEntry. Entry can be a concrete type in libcollections shared by all the maps:
This would be the optimal code-reuse configuration. It would also have a nice documentation benefit in that there would be a reasonable place to put the Entry API docs. I'm not opposed to this, although I don't have particularly strong feelings either way. |
That discussion is not particularly relevant, something like
Well, I was actually only thinking of one trait, which gets implemented for each top level // hashmap.rs
use std::collections;
enum Entry<'a, K, V> {
Vacant(VacantEntry<'a, K,V>),
Occupied(Occupied<'a, K, V>)
}
impl<'a, K, V> collections::Entry<'a, K, V> for Entry<'a, K, V> {
fn into_mut(self) -> Result<&'a mut V, Self> {
match self {
Occupied(o) => Ok(o.into_mut()),
Vacant(v) => Err(Vacant(v))
}
}
fn set /* etc. */
} (I don't know if that precise trait definition is sensible for the one-trait scheme.) But maybe having the generic |
I want to be very careful here, because:
That said, and ignoring the issue of whether to try to put this in a trait, it seems like if you add the suggested basic methods to impl<'a, K, V> Entry<'a, K, V> {
/// Returns Some if there's already data here.
fn into_mut(self) -> Result<&'a mut V, VacantEntry<'a, K, V>>;
fn set(self, value: V) -> &'a mut V;
} then hash_map.entry(key).into_mut()
.unwrap_or_else(|v| v.set(vec![]))
.push(value) which is getting pretty ergonomic (and computes the default value lazily). (Note that I adjusted the type of (Update: I meant |
@aturon 👍, though |
@sfackler, interesting point -- we don't have an explicit convention for conversions that can fail, but |
Hm, I wonder if we could use The largest downside I can think of is maybe |
That's worth thinking about, although it would actually worsen the ergonomics in this case: This hash_map.entry(key)
.into_mut()
.unwrap_or_else(|v| v.set(vec![]))
.push(value) would become this: hash_map.entry(key)
.map(|o| o.into_mut())
.unwrap_or_else(|v| v.set(vec![]))
.push(value) |
I'm tempted to let this sit for a month or so to let people really explore the usage state space (I've already seen some cool stuff I never anticipated before). That said, I think the proposed
Which is a bit unfortunate, although I'm starting to lean against the "disjoint" style as awkward. With
|
@cmr has also suggested OccupiedEntry impl'ing Deref(Mut). This is an interesting idea. |
Just a heads up to those interested. This is my working proposal API 2.0: The old Entry API:
Based on feedback and collections reform landing, we propose the following new API:
|
FYI: Cargo uses |
We got |
Closing, this is done. |
The deprecation of methods like
find_or_insert
resulted in more verbose code (needing more imports too), e.g.hash_map.find_or_insert(key, vec![]).push(value)
becomeswhich is slightly annoying. We could have an
Entry
trait that has an interface sufficient to have default methods for functionality like the oldfind_or_insert
(like Iterator just has thenext
method which is enough for things likemap
&collect
etc), so the above would becomeThis would also mean that all entries get these methods, avoiding the historical problem of
HashMap
having everything, withTreeMap
missing a lot.(We should keep the requirements for the basic
Entry
trait as minimal as possible, so it can be used as widely as possible; if there is fancy functionality that needs more restrictions, we can theoretically have extra traits, similar toDoubleEndedIterator
etc.)The text was updated successfully, but these errors were encountered: