在Rust中取消引用字符串和HashMaps

时间:2016-05-30 08:30:31

标签: hashmap rust dereference borrowing

我试图了解HashMaps在Rust中是如何工作的,我已经提出了这个例子。

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
}

当我尝试编译最后一行未注释的代码时,我收到以下错误

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

docs的特征实施部分将解除引用设为fn deref(&self) -> &str

那么这里发生了什么?

3 个答案:

答案 0 :(得分:13)

错误是由类型推断期间编译器选择String以上的泛型函数HashMap::get引起的。但您希望HashMap::get超过str

所以只需改变

println!("{:?}", roman2number.get(&r0.to_string()));

println!("{:?}", roman2number.get::<str>(&r0.to_string()));

使其明确。这有助于编译器选择正确的函数。

查看Playground here

我认为强制Deref<Target>只有在我们知道目标类型时才会发生,因此当编译器试图推断使用哪个HashMap::get时,它会将&r0.to_string()视为类型{ {1}}但永远不会&String&str未实现&'static str。这会导致类型错误。当我们指定Borrow<String>时,如果可以将强制应用于HashMap::get::<str>以获得匹配的&str,则此函数需要&String

您可以查看Deref coercionString Deref了解详情。

答案 1 :(得分:5)

其他答案是正确的,但我想指出您有一个不需要的to_string(您已经collect加入String)和另一种强制方式使用&str

as
let r0: String = roman_num.chars().take(1).collect();
println!("{:?}", roman2number.get(&r0 as &str));

这个的情况下,我可能只是重写地图以包含char作为关键:

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));
    }
}

请注意,不需要为地图提供明确的类型,它将被推断出来。

答案 2 :(得分:3)

get方法的定义如下

fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq

第一部分是您传递的对象类型:QQ有约束。 Q上的条件是

  1. 密钥类型K需要在Q
  2. 上实施Borrow特征
  3. Q需要实施HashEq特征。
  4. 将其替换为实际类型意味着密钥类型&'static str需要实现Borrow<String>。根据{{​​1}}的定义,这意味着Borrow需要转换为&'static str。但我读过的所有文档/文字都表明,无论您使用&String的所有地方,都应使用&String代替&str。因此提供&str - &gt;是没有意义的。 &String转换,即使它有时会让生活变得更轻松。

    由于每个reference type is borrowable as a shorter lived reference type。),当&str是密钥类型时,您可以传递&'static str,因为&'static str实现了Borrow<str>