-
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
Keyless Entry #1533
Keyless Entry #1533
Conversation
All possible changes have tradeoffs. For me, breaking backwards compatibility is a non-starter. Perhaps there is a way of extending the current Entry API with genericity and conversion traits like so: enum Entry<'a, K, V, Q = K> {
Occupied(OccupiedEntry<'a, K, V>),
Vacant(VacantEntry<'a, K, V, Q>),
}
// method on HashMap
fn entry<Q>(&mut self, key: Q) -> Entry<K, V, Q>
where K: Borrow<Q>
// method on VacantEntry
fn or_insert<W>(self, value: W) -> &'a mut V
where (K, V): From<(Q, W)> Today's Entry is a special case of this interface with edit 30.06: fixed a bound |
Let's reconsider what needs to be done. The current Entry API is insufficient in two scenarios:
The problem in the first scenario is simple, because it only concerns
struct OccupiedEntry<'a, K, V> {
key: Option<K>,
elem: FullBucket<...>
}
impl<'a, K, V> OccupiedEntry<'a, K, V> {
pub fn take_key(&mut self) -> Option<K> {
self.key.take()
}
} |
The original purpose of the entry API was to prevent double lookups. This can't fix that while |
I completely agree - I included the backwards incompatible alternative just for completeness. The main proposal is not backwards incompatible in any way.
This sort of API would probably be ideal from a usability standpoint, but I don't see any way that it can work. You take There may also be compatibility issues with broken type inference, but I don't know how much of a problem that would be.
Exactly. This RFC aims to solve only the second problem.
I'm confused as to why you think that - not only can this API be used to prevent double lookups, but fn entry_or_clone(&mut self, key: &K) -> Entry<K, V> where K: Clone {
match self.keyless_entry(key) {
KeylessEntry::Occupied(entry) => Entry::Occupied(entry)
KeylessEntry::Vacant(entry) => Entry::Vacant(entry.with_key(key.clone()))
}
} |
Sorry, I didn't read the drawbacks. IMO, not being able to guarantee that the key is the same is way too much of a foot gun to be acceptable. |
could you clarify what you mean by
|
@jmesmon A user could write: self.keyless_entry(&key1).or_insert_with(||(key2, 0)) += 1; and end up with key2 in key1's slot. |
Alternate alternative: combine this proposal and the |
|
||
- Replace the `Entry` API entirely. This would get rid of the inherent duplication of having two `Entry` APIs, | ||
but would be backwards incompatible. | ||
- Create an `OccupiedKeylessEntry` that is separate from `OccupiedEntry` to help keep to two different `Entry`s |
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.
Please add a note saying this alternative is an addition to the main proposal.
(or make three categores of alternatives -- backwards compatible, incompatible, and additions to the main proposal)
I haven't given up on this issue. It can be decomposed into two essential steps.
Let's focus on the first step. An API with ideal usability has to take a key that is either borrowed or owned. pub fn entry<'a, Q, B>(&mut self, key: Q) -> Entry<K, V, B>
where K: Into<Cow<'a, B>>,
Q: Into<Cow<'a, B>>,
B: Eq + Hash + ToOwned + 'a,
{
self.reserve(1);
let key_cow: Cow<'a, B> = key.into();
self.search_mut(&key_cow).into_entry(key_cow).expect("unreachable")
} The compiler will complain about pub fn entry<Q = K, B: ?Sized = K>(&mut self, key: Q) -> Entry<K, V, Q>
where Q: Borrow<B>,
K: Borrow<B>,
B: Eq + Hash,
{
self.reserve(1);
self.search_mut(Borrow::<B>::borrow(&key)).into_entry(key).expect("unreachable")
} These default type parameters are necessary, because some types have two different implementations of fn foo<K>(map: HashMap<String, …>, key: String) {
map.entry(key);
// Types can be inferred in two ways:
// first
map.entry::<String, String>(key);
// second, due to `String: Borrow<str>`
map.entry::<String, str>(key);
} Unfortunately, default_type_parameter_fallback is unstable as a gated feature. As for the second step, the Entry API should be able to provide a function that transforms an |
So I've got a suggestion (playground) which may not require an additional method. The idea is to introduce a new pub trait IntoOwned<T>{
fn into_owned(self) -> T;
}
impl<T> IntoOwned<T> for T {
fn into_owned(self) -> T { self }
}
impl<'a, T> IntoOwned<T> for &'a T where T: Clone {
fn into_owned(self) -> T {
(*self).clone()
}
}
impl<'a> IntoOwned<String> for &'a str {
fn into_owned(self) -> String {
self.to_owned()
}
}
/* ... */ The // Entry would have to gain Q: IntoOwned<K> as a type param with default Q=K.
fn entry<'a, Q>(&'a mut self, key: Q) -> Entry<'a, K, V, Q>
where K: Hash + Eq,
Q: Hash + Eq + IntoOwned<K>
{
// Build an `Entry` which stores `key` and calls `into_owned`
// on vacant insertion only.
} The trouble with this is that we'd need to duplicate all the existing impl<'a, T> IntoOwned<T::Owned> for &'a T where T: ToOwned conflicts with As it stands, we could keep |
Just to bring in some conversation from IRC. The only coherence trouble the impl runs into is of the form impl<T> IntoOwned<T> for T {
fn into_owned(self) -> T { self }
}
impl<'a, T> IntoOwned<T::Owned> for &'a T
where T: ToOwned,
T::Owned != &'a T
{
fn into_owned(self) -> T::Owned {
(*self).to_owned()
}
} |
I worked around the coherence issues an wrote up an RFC #1769 |
Team member @aturon has proposed to close this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
I approve of this being closed - I think #1769 is a better solution. |
Ok, all checkboxes checked, closing in favor of #1769 |
Fixes #1203.