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

Tracking issue for #![feature(entry_insert)] #65225

Open
1 of 3 tasks
passcod opened this issue Oct 8, 2019 · 21 comments
Open
1 of 3 tasks

Tracking issue for #![feature(entry_insert)] #65225

passcod opened this issue Oct 8, 2019 · 21 comments
Labels
A-collections Area: std::collections. B-unstable Feature: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@passcod
Copy link
Contributor

passcod commented Oct 8, 2019

This is a tracking issue for the Entry::insert_entry method on HashMap and BTreeMap introduced in #60142 (comment).

@jonas-schievink jonas-schievink added A-collections Area: std::collections. B-unstable Feature: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Oct 8, 2019
@TethysSvensson
Copy link
Contributor

This does not appear to be compatible with #44286, as the following code will panic.

#![feature(map_entry_replace, entry_insert)]
use std::collections::HashMap;

fn main() {
    HashMap::new().entry(0).insert(0).replace_key();
}

It seems to me that the two APIs are fundamentally incompatible, or at least they are currently incompatible without panicking.

The API in #44286 assumes that an OccupiedEntry will always refer to two keys, the existing one and the one used for the lookup, while this feature will break that assumption. In fact, the key field in OccupiedEntry and RustcOccupiedEntry in hashbrown could be a K instead of an Option<K> if this API was not supported.

@TethysSvensson
Copy link
Contributor

It seems to me that it is a bit silly to use an Option to track at run-time what we should already know at compile time. Wouldn't it make more sense to have a different type from OccupiedEntry, which did not contain a key?

I could imagine this would also be useful as a stepping stone towards implementing something like rust-lang/rfcs#1769.

@passcod
Copy link
Contributor Author

passcod commented Jan 7, 2020 via email

@TethysSvensson
Copy link
Contributor

@passcod I believe you mostly are correct on both parts of your statement.

I do not think the Entry::insert function can be implemented in safe rust, as it allows the creation of an OccupiedEntry without an associated key. I cannot see how it would be possible to do that in safe rust.

The OccupiedEntry::replace_entry and OccupiedEntry::replace_key functionality can be implemented in safe rust, using a combination of OccupiedEntry::remove and HashMap::insert.

However I do not think it is possible to implement it as a function in safe rust. You would need to pass both the HashMap and the Entry as an argument in the same function, however the lifetime on the Entry prevents use of the HashMap until the Entry has been dropped.

@Nokel81
Copy link
Contributor

Nokel81 commented Jan 28, 2020

Why isn't the signature of this method pub fn insert(self, value: V) -> &'a mut V like the or_*** methods?

@passcod
Copy link
Contributor Author

passcod commented Jan 28, 2020

The motivation is to be able to reference the key (or the entry, more generally) even after inserting a value.

@lamafab
Copy link

lamafab commented Jun 9, 2020

The current implementation is kind of limited, you cannot do something like this:

fn insert(&mut self, msg: Message) -> &Message {
  self.map.entry(msg).insert(0).key()
error[E0515]: cannot return value referencing temporary value

It would be convenient if you could implement this in a way which says "insert the key and return a reference of the key from within the HashMap".

Currently, you'd have to do this ugly work-around:

fn insert(&mut self, msg: Message) -> &Message {
  let msg2 = msg.clone();
  self.map.entry(msg).insert(0); // or just `.or_insert()`
  self.map.get_key_value(&msg2).unwrap().0

@shepmaster
Copy link
Member

@lamafab I think you want a function like

impl<'a, K, V> OccupiedEntry<'a, K, V> {
    pub fn into_key(self) -> &'a K { /* ... */ }
}

@joshtriplett
Copy link
Member

We reviewed this in today's @rust-lang/libs-api meeting.

We'd like to see a similar method added to BTreeMap::Entry, but that's not a blocker for stabilizing the method for HashMap::Entry.

We feel like this should be named insert_entry, rather than just insert, since it returns an OccupiedEntry, unlike the similar insert method that returns just a value.

Could you rename this method to insert_entry, and then provide a stabilization PR?

Could you also consider adding an insert_entry method to VacantEntry?

@passcod
Copy link
Contributor Author

passcod commented Sep 1, 2021

Heyho! Awesome 🦀 I'll try to get to it sometime this month.

Edit: still on my radar, probably end of October.

@bluecheetah001
Copy link

I recently needed something similar to lamafab where I wanted a reference to the key in the map after inserting
I was wondering if insert_entry could instead return a new type which I will call EntryRef.
EntryRef.getKey could use the map's lifetime
EntryRef.replace_key wouldn't exist as it doesn't own a key

OccupiedEntry.insert_entry could return (K, EntryRef) or something analogous
VacantEntry.insert_entry could return EntryRef
Entry.insert_entry could return (Option<K>, EntryRef) or something analogous

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Dec 21, 2021
Stabilise entry_insert

This stabilises `HashMap:Entry::insert_entry` etc. Tracking issue rust-lang#65225. It will need an FCP.

This was implemented in rust-lang#64656 two years ago.

This PR includes the rename and change discussed in rust-lang#65225 (comment), happy to split if needed.
@dhardy
Copy link
Contributor

dhardy commented Feb 21, 2022

Suggestion: replace insert_entry with simpler insert as @Nokel81 suggested above: pub fn insert(self, value: V) -> &'a mut V

This covers most uses (anything not needing to access the key), and since the only motivation for this method is convenience, there's no need to worry about that case. This then removes the conflict with #44286.

@passcod
Copy link
Contributor Author

passcod commented Feb 21, 2022

Didn't want to jump the horse during the unstabilisation moment but I'm unfortunately not terribly interested in this feature anymore, so am unsubbing from it all. This has nothing to do with anyone involved and only to do with me/time, and I generally/superficially agree with the concerns raised for the stabilisation revert. Thanks for all the fish 🐬

@m-ou-se m-ou-se added the I-libs-api-nominated The issue / PR has been nominated for discussion during a libs-api team meeting. label Feb 24, 2022
@m-ou-se
Copy link
Member

m-ou-se commented Mar 9, 2022

We briefly discussed this issue last week in the Library API meeting. We'd like to see a more complete proposal for an alternative API, such as the EntryRef idea that was mentioned before.

@m-ou-se m-ou-se removed the I-libs-api-nominated The issue / PR has been nominated for discussion during a libs-api team meeting. label Mar 9, 2022
@maboesanman
Copy link

One very odd part of this api is that insert_entry gives you an OccupiedEntry but remove_entry doesn't give you a VacantEntry

This could be useful in situations where you remove an item, but may or may not need to replace it. In this case you can use .entry(k).remove_entry(), then keep the resulting VacantEntry around for use later.

Useful additions to this api would be next_occupied and prev_occupied on btree_map::Entry, and implementations of all of these functions on HashSet and BTreeSet, though that is likely a question for a more stable version of this api.

On the topic of key ownership, I think the most consistent approach is to have the OccupiedEntry borrow it's key from the collection, and VacantEntry own its key. have the entry method be generic over an std::borrow::ToOwned type for the key. At the point entry needs to decide between OccupiedEntry and VacantEntry, it will either drop the search key in favor of borrowing the key from the collection (for occupied) or call to_owned on the search key and move it into the entry (for vacant).

insert_entry on VacantEntry will move the key from the entry into the collection, borrow it, and store the borrow in an OccupiedEntry. remove_entry on OccupiedEntry will move the key from the collection to the entry.

insert_entry on Entry::OccupiedEntry is an in place change to the value, so that doesn't really need to do anything special with keys, and remove_entry on Entry::VacantEntry could just return a None variant.

one final note: I am in general not a fan of any method with a _entry suffix that returns anything but an entry. things which return (&K, &mut V) seem mostly like half measures whose functionality would follow from returning a proper Entry.

@maboesanman
Copy link

I've created a mockup for what I think this api could be: https://github.com/maboesanman/InPlace
and cargo docs here: https://maboesanman.github.io/InPlace/in_place/

curious what people's thoughts are.

@Nokel81
Copy link
Contributor

Nokel81 commented Mar 15, 2022

I think it looks good, for what it is worth. With that I see a nice solution to a complaint that I have seen about that's hashmap type where the entry API requires allocation even just to check if some boxed key (such as String) is present.

@maboesanman
Copy link

I've put a bunch more thought into the types for the entry api, and am planning on putting together a proper RFC.

the same repo as above (and the documentation link, if GitHub Pages finally uploads it) apply, now updated to reflect changes made to the design, which has matured a bit. Now I think it's likely mostly a matter of extending with helpers and improving ergonomics.

right now I'm pretty happy with it as an extension to the standard library collections, and am part of the way through implementing the required traits in a fork of HashBrown.

Have a look if you're interested!

@MarcelCoding
Copy link

MarcelCoding commented Jul 28, 2022

Hi, I really like this feature. Are there any news on this since mid-May?

@mcclure
Copy link

mcclure commented Dec 9, 2022

Am I reading correct there is no movement on this since March? This seems like an unusual omission from the stable API (right now Entry can conditionally insert but not unconditionally insert). I understand the intent of the API is for inserting when key is not present, but there are reasonable purposes for unconditional insert as well, for example, checking the value then inserting if a condition is met:

// Code fragment I actually wrote today, before discovering insert_entry is nightly only
fn map_write_if_greater<K,T>(map:HashMap<K,T>, key:K, v:T) where K:Eq+std::hash::Hash, T:PartialEq+PartialOrd {
	let entry = map.entry(key);
	if match entry { Entry::Vacant(_) => true, Entry::Occupied(v2) => v>*v2.get()} {
		entry.insert_entry(v); // Avoid second hash lookup
	}
}

It seems like the holdup here is not whether the function should go in, but rather disagreement over the return value / naming; but it's been a long time without that disagreement being resolved. (I personally agree with nokel that pub fn insert(self, value: V) -> &'a mut V is superior to insert_entry -> Occupied(T), but also I don't personally care that much as long as the feature is present.)

@Ekleog-NEAR
Copy link

@passcod In the top-comment, the checked checkbox writing that insert_entry was stabilized in 1.59 should be unchecked: it was destabilized in #94105 (just writing that so other people don’t feel compelled to try it out on play.rlo because the docs and this tracking issue currently have different opinions on whether this is stable yet)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-collections Area: std::collections. B-unstable Feature: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests