I have a use case where I need to do the following things on a BTreeMap
Start with a reference to a key
Get or insert default and modify the value
Get a reference to the key that is in the HashMap (it's a unicase::Ascii, so potentially slightly different, I want to preserve the first case specified)
Maybe I'm missing something in the entry API but I can't seem to do this without constructing two owned keys.
type Map = BTreeMap<Ascii<String>, Vec<&'static str>>;
fn do_thing(map: &mut Map, key: &str, value: &'static str) -> String {
let values = map.entry(Ascii::new(key.to_owned())).or_default();
// check if we need to insert particular value
let i = match values.iter().position(|v| *v == value) {
Some(i) => i,
None => {
let i = values.len();
values.push(value);
i
}
};
// now I need the key, in it's originally specified case
let (orig_key, _) = map.get_key_value(&Ascii::new(key.to_owned())).unwrap();
// Do something with original key and i
// ...
format!("{}-{}", orig_key, i)
}
BTreeMap::entry needs the key to be owned, because it will insert it into the map if no entry was encountered when VacantEntry::insert is called. There have been several attempts to enhance the entry API in this regard, but they have stalled long ago:
The technical reason is that it returns a &'a mut V, where 'a is the lifetime of the map reference. There could be a variant that mutably borrows the entry but returns a reference with the lifetime of the borrowed entry, but it just isn't there.
Note however that the result of entry is a Entry enum that you can match on to get either a VacantEntry or an OccupiedEntry. Notably VacantEntry offers what you want: it gives you access to both the key and the inserted value. However unfortunately converting a VacantEntry to a OccupiedEntry by inserting a value is only available on the nightly compiler. You can probably work around this by computing i before inserting the value in case you're working with a VacantEntry, but that will probably require a bit of duplicated code.