Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dereferencing strings and HashMaps in Rust

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?

like image 201
skanur Avatar asked May 30 '16 08:05

skanur


2 Answers

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.

like image 117
WiSaGaN Avatar answered Sep 21 '22 12:09

WiSaGaN


The other answers are correct, but I wanted to point out that you have an unneeded to_string (you've already collected 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.

like image 37
Shepmaster Avatar answered Sep 18 '22 12:09

Shepmaster