I'm trying to understand how HashMaps work in Rust and I have come up with this example.
use std::collections::HashMap;
fn main() {
let mut roman2number: HashMap<&'static str, i32> = HashMap::new();
roman2number.insert("X", 10);
roman2number.insert("I", 1);
let roman_num = "XXI".to_string();
let r0 = roman_num.chars().take(1).collect::<String>();
let r1: &str = &r0.to_string();
println!("{:?}", roman2number.get(r1)); // This works
// println!("{:?}", roman2number.get(&r0.to_string())); // This doesn't
}
When I try to compile the code with last line uncommented, I get the following error
error: the trait bound `&str: std::borrow::Borrow<std::string::String>` is not satisfied [E0277]
println!("{:?}", roman2number.get(&r0.to_string()));
^~~
note: in this expansion of format_args!
note: in this expansion of print! (defined in <std macros>)
note: in this expansion of println! (defined in <std macros>)
help: run `rustc --explain E0277` to see a detailed explanation
The Trait implementation section of the docs gives the dereferencing as fn deref(&self) -> &str
So what is happening here?
The error is caused by that generic function HashMap::get
over String
is selected by the compiler during type inference. But you want HashMap::get
over str
.
So just change
println!("{:?}", roman2number.get(&r0.to_string()));
to
println!("{:?}", roman2number.get::<str>(&r0.to_string()));
to make it explicit. This helps the compiler to select the right function.
Check out Playground here.
It looks to me that coercion Deref<Target>
can only happen when we know the target type, so when compiler is trying to infer which HashMap::get
to use, it sees &r0.to_string()
as type &String
but never &str
. And &'static str
does not implement Borrow<String>
. This results a type error. When we specify HashMap::get::<str>
, this function expects &str
, when coercion can be applied to &String
to get a matching &str
.
You can check out Deref
coercion and String
Deref for more details.
The other answers are correct, but I wanted to point out that you have an unneeded to_string
(you've already collect
ed into a String
) and an alternate way of coercing to a &str
, using as
:
let r0: String = roman_num.chars().take(1).collect();
println!("{:?}", roman2number.get(&r0 as &str));
In this case, I'd probably just rewrite the map to contain char
as the key though:
use std::collections::HashMap;
fn main() {
let mut roman2number = HashMap::new();
roman2number.insert('X', 10);
roman2number.insert('I', 1);
let roman_num = "XXI";
for c in roman_num.chars() {
println!("{:?}", roman2number.get(&c));
}
}
Note there's no need to have an explicit type for the map, it will be inferred.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With