Skip to content
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

Allow replacing HashMap entries #44278

Merged
merged 3 commits into from
Sep 28, 2017
Merged

Allow replacing HashMap entries #44278

merged 3 commits into from
Sep 28, 2017

Conversation

Binero
Copy link
Contributor

@Binero Binero commented Sep 2, 2017

This is an obvious API hole. At the moment the only way to retrieve an entry from a HashMap is to get an entry to it, remove it, and then insert a new entry. This PR allows entries to be replaced.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @BurntSushi (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

///
/// ```
#[stable(feature = "rust1", since = "1.20.0")]
pub fn replace(mut self, value: V) -> (K, V) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new API should be #[unstable].

@kennytm
Copy link
Member

kennytm commented Sep 3, 2017

Why not apply this to BTreeMap too?

@Binero
Copy link
Contributor Author

Binero commented Sep 3, 2017

@kennytm It is non-trivial to implement it for BTreeMap. BTreeMap's entry does not have ownership of the key. Giving it ownership would break BTreeMap::replace.

Not sure how to implement recovery of a BTreeMap's entry without breaking the existing APIs. Both of these map APIs are littered with methods that take by ownership, use a borrow and then throw away the value they took ownership of.

@Binero
Copy link
Contributor Author

Binero commented Sep 3, 2017

I am quite inexperienced with the std, and I am unsure how such an unstable attribute would look.

Adding #[unstable(feature = "rust1", issue = "44286")] gives a compile error during tests:

error: use of unstable library feature 'rust1' (see issue #44286)

@kennytm
Copy link
Member

kennytm commented Sep 3, 2017

@Binero You would first need to pick a name of the feature, say entry_replace. When nightly user tries to use your API, they will need to add #![feature(entry_replace)].

The syntax of #[unstable] attribute is:

#[unstable(feature = "entry_replace", reason = "recently added", issue = "44286")]

The doctest examples will need to be modified to include #![feature(entry_replace)]:

    /// # Examples
    ///
    /// ```
    /// #![feature(entry_replace)]
    /// # fn main() {
    /// use std::collections::HashMap;
    /// use std::collections::hash_map::Entry; 
    /// // ...
    /// # }
    /// ```

@arielb1 arielb1 added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Sep 5, 2017
@Binero
Copy link
Contributor Author

Binero commented Sep 7, 2017

Not sure why this is waiting on author?

@carols10cents
Copy link
Member

@Binero I'm not sure either. Maybe because of the test failure? It looks like an osx worker had a network failure though; I've restarted that job.

I think this is ready for review again @BurntSushi !

@carols10cents carols10cents added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 11, 2017
Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few nits, and mechanically, this looks fine to me.

Is this something we definitely want though? Do you have a use case in mind where this method is useful?

/// # Examples
///
/// ```
/// # #![feature(map_entry_replace)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we include the feature toggle in the docs so that end users know that it is necessary. So I'd remove the # prefix here.

/// }
///
/// assert_eq!(map.get("poneyland"), Some(&16));
///
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line?

/// use std::collections::hash_map::Entry;
///
/// let mut map: HashMap<String, u32> = HashMap::new();
/// map.insert(String::from("poneyland"), 15);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we use "poneyland".to_string()?

/// let mut map: HashMap<String, u32> = HashMap::new();
/// map.insert(String::from("poneyland"), 15);
///
/// if let Entry::Occupied(entry) = map.entry(String::from("poneyland")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, use to_string.

@BurntSushi BurntSushi added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 11, 2017
@Binero
Copy link
Contributor Author

Binero commented Sep 12, 2017

@BurntSushi The Entry-API serves as a temporary result, to prevent having to lookup entries in the HashMap multiple times.

Right now, to get a key out of a HashMap, you have to do the following:

  1. Create an entry to the key-value pair you want to remove. (lookup)
  2. Remove it. (shifts elements following the removed entry to the left)
  3. Insert a new entry. (lookup + shifts those same elements back to the right)

With this addition, the process is simplified to the following:

  1. Create an entry to the key-value pair you want to remove. (lookup)
  2. Replace it. (O(1))

@carols10cents carols10cents added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 18, 2017
pub fn replace(mut self, value: V) -> (K, V) {
let (old_key, old_value) = self.elem.read_mut();

let old_key = mem::replace(old_key, self.key.unwrap());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One can take the key out of the entry using take_key before replace, so this key.unwrap() can fail. It needs a custom message at least.

@arthurprs
Copy link
Contributor

I think this (sort of) already possible using occupied.take_key() and replace(occupied.get_mut(), new_value).

The difference is that take_key will return the key passed to entry() instead of the one inside the hashmap.

@Binero
Copy link
Contributor Author

Binero commented Sep 23, 2017

@arthurprs take_key is only called internally, and the entry is always consumed after usage. Furthermore, take_key returns the key the entry was created with, which doesn't address the issue at all. If one wanted to have the new key, one wouldn't create an Entry. The point of the replace function is to recover the old key.

@arthurprs
Copy link
Contributor

Indeed, thanks for clarifying.

Copy link
Contributor

@arthurprs arthurprs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@BurntSushi
Copy link
Member

@Binero That's good enough for me! Thanks for clarifying. Should this get an entry in the unstable book before merging?

@BurntSushi BurntSushi added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 25, 2017
@alexcrichton
Copy link
Member

@BurntSushi oh we typically don't have an entry per unstable library API as the docs here typically suffice for that, in which case I'll..

@bors: r=BurntSushi

Thanks for the PR @Binero!

@bors
Copy link
Contributor

bors commented Sep 28, 2017

📌 Commit d3de465 has been approved by BurntSushi

@alexcrichton alexcrichton added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 28, 2017
@bors
Copy link
Contributor

bors commented Sep 28, 2017

⌛ Testing commit d3de465 with merge 3c96d40...

bors added a commit that referenced this pull request Sep 28, 2017
Allow replacing HashMap entries

This is an obvious API hole. At the moment the only way to retrieve an entry from a `HashMap` is to get an entry to it, remove it, and then insert a new entry. This PR allows entries to be replaced.
@bors
Copy link
Contributor

bors commented Sep 28, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: BurntSushi
Pushing 3c96d40 to master...

@bors bors merged commit d3de465 into rust-lang:master Sep 28, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants