What is the proper way to index into a HashMap<&String, V>
with a &str
? Rust reports that &String
is not Borrow<str>
, which is required for indexing. (This seems a bit silly to me; if T: Borrow<U>
then surely &T: Borrow<U>
should also hold?)
use std::collections::HashMap;
fn main() {
let strings = vec!["x".to_owned(), "y".to_owned(), "z".to_owned()];
let m = [0, 1, 2]
.into_iter()
.map(|i| (&strings[i], i))
.collect::<HashMap<&String, usize>>();
let zero = m["x"];
}
error[E0277]: the trait bound `&String: Borrow<str>` is not satisfied
--> src/main.rs:9:18
|
9 | let zero = m["x"];
| ^^^ the trait `Borrow<str>` is not implemented for `&String`, which is required by `HashMap<&String, usize>: Index<&_>`
|
= help: the trait `Borrow<str>` is implemented for `String`
= help: for that trait implementation, expected `String`, found `&String`
= note: required for `HashMap<&String, usize>` to implement `Index<&str>`
Obviously, because I constructed this particular HashMap, I could go back and change its key type to &str
instead. But supposing I'm handed a HashMap<&String, V>
, what is the proper way to index it? Constructing a whole String
just to take a reference to it seems wasteful.
With #![feature(hash_raw_entry)]
, it is possible to access keys directly by hash (with a followup check for equality), which lets one get around borrowing limitations.
#![feature(hash_raw_entry)]
use std::hash::{Hash, Hasher, BuildHasher};
use std::collections::HashMap;
fn main() {
let strings = vec!["x".to_owned(), "y".to_owned(), "z".to_owned()];
let m = [0, 1, 2]
.into_iter()
.map(|i| (&strings[i], i))
.collect::<HashMap<&String, usize>>();
let key = "x";
let mut hasher = m.hasher().build_hasher();
key.hash(&mut hasher);
let hash = hasher.finish();
let zero = m.raw_entry().from_hash(hash, |found_key| found_key == &key);
println!("{zero:?}");
// Some(("x", 0))
}