At the moment the only way to replace a map entry with a new one, swallows the old key. In cases the user wants to keep the old key, the only way to do so is to first remove_entry and to then insert a new one. This requires two map look-ups.
As the Entry API aims to make operations that would otherwise require two look-ups into operations that require only one, the replacing of an entry is an obvious hole in the API.
While it is indeed a missing API, there are several issues with this new API implemented in PR #44278:
take_key was called beforeinsert function which replaces only value)VacantEntryself, while it could take &mut self, so replace could be called again if it is needed for some reasonI think HashMap::replace should be different:
impl OccupiedEntry<...> {
fn replace_value(&mut self, new_value: V) -> V {
replace value with new value, do not touch the key.
}
}
And probably that's it.
If we need to replace key for some reason, replace_key operation needs to be added to both OccupiedEntry and VacantEntry.
take_key cannot be called before, all functions that call it consume the entry.replace_value beats the point.VacantEntry does not have a key, so it's unclear how the function would work.take_key cannot be called before, all functions that call it consume the entry.
Oh, it is not public API, thanks.
The point is to be able to swap out the key without multiple lookups. replace_value beats the point.
But this function does not allow to replace key without replacing value.
BTW, there's already a function to replace value, it's called insert.
So if we had replace_key instead of current replace, then current replace could be emulated as:
occupied_entry.insert(new_value);
occupied_entry.replace_key()
replace_key is not possible now, and replace is redundant if we had replace_key.
A VacantEntry does not have a key, so it's unclear how the function would work.
Please ignore that.
I will take a look at replace_key when I have time, probably next week.
@stepancheg I think it's probably better to have replace_key consume the entry, because the semantics of the function would otherwise change after the first call.
This has been in Nightly for long enough that I鈥檓 inclined to stabilize, but I don鈥檛 quite understand the use case. @Binero, can you explain some more why it can be important to recover the old key rather than the new one (passed to HashMap::entry() which hashes and compares equal?
@SimonSapin
It is quite a niche function, so it's certainly not instantly obvious where this would be useful.
That said, it has some uses when you want to for example lower the memory footprint of a HashMap by swapping out an Rc<T> with another, equivalent, Rc<T> that you are already using elsewhere.
If either T or the HashMap is large enough, this could reduce the memory footprint of the application by quite a lot.
Not sure if this was the original intent, but I have a use case for replace_key where I need the key back (by value) in certain failure conditions. I have code like:
match map.entry(key) {
Entry::Occupied(entry) =>
entry.into_mut().do_thing_which_can_fail().map_err(|_| entry.replace_key()),
Entry::Vacant(entry) => ...,
}
Without replace_key, I'd need to do map.entry(key.clone()) instead so that I could still have key around by value.
I may be off-base here, but I feel like there is a requirement for these methods that the key continue to have the same hash? If so, that should almost certainly be stated in the documentation.
That is correct.
Ah, reading it again, I guess the reason this is okay is because you can only get an OccupiedEntry in the first place if the provided key already hashes to the appropriate value. Not mentioning it in the docs seems fine then!
It's not super easy to spot from this issue alone, but the replace_key method is incompatible with another unstable Entry::insert method. See: https://github.com/rust-lang/rust/issues/65225#issuecomment-571584668
I just happened upon a use case for this feature while generalizing the code linked here to arbitrary key types. Basically, it's a hash table with support for scoping and shadowing, and if a newly inserted key shadows an existing one, the old key and value are put into a separate Vec, and when the shadowing scope is popped, they are inserted back into the map. But insert() only gets one copy of the key as input, and that key can't simultaneously exist in two places: in the HashMap with the newly inserted value, and in the Vec alongside the previous/shadowed one. So in the case where the key already exists in the map, I need to be able to get the old value and the old key back out so I can put both of them into the Vec.
Most helpful comment
Not sure if this was the original intent, but I have a use case for
replace_keywhere I need the key back (by value) in certain failure conditions. I have code like:Without
replace_key, I'd need to domap.entry(key.clone())instead so that I could still havekeyaround by value.