use std::borrow::Cow;
use std::collections::HashMap;
fn main() {
let map: HashMap<(usize, Cow<str>), String> = HashMap::new();
// how to avoid the allocation?
map.get(&(1, "key".into()));
}
To support string literals and owned strings as keys (which is more flexible than your current API that only allows owned strings as keys), Cow<'static, str> will be enough. So no need to add lifetime parameters to types that store such a hash map.
I think the design of HashMap unfortunately doesn't allow this because the Borrow trait must return a reference to part of the key rather than another type that contains such references. I consider this a design flaw of Borrow.
If you use hashbrown::HashMap, which is the underlying implementation in the standard library, it will allow such look-ups using anything that implements Equivalent. That trait covers the normal Borrow+Eq relationship, but you can also extend it by implementing something like struct Query(usize, &str) to use in your get calls.
Would that even be possible to express in the language?
impl<OI, BI> Borrow<(BI)> for (OI)
where OI: Borrow<BI> {
type Borrowed = (BI);
}
While thats technically possible with a good old impl! macro for tuples up to size N, it's not really expressing the relation. We'd need variadic generics or something like that.
'static doesn't suffice in the actual example.
We ended up using a different data structure, where the usize indexes a vector with maps.
But the hashbrown suggestion is very interesting!