16

我试图了解 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

文档的 Trait 实现部分将取消引用指定为fn deref(&self) -> &str

那么这里发生了什么?

4

3 回答 3

23

HashMap::get该错误是由编译器在String类型推断期间选择的泛型函数引起的。但是你想HashMap::get结束str

所以只要改变

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

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

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

在这里查看游乐场

在我看来,强制Deref<Target>只有在我们知道目标类型时才会发生,所以当编译器试图推断HashMap::get要使用哪个时,它会看到&r0.to_string()type&String但从不&str。并且&'static str不执行Borrow<String>。这会导致类型错误。当我们指定时HashMap::get::<str>,这个函数期望&str,当强制转换时可以&String得到一个匹配&str

您可以查看DerefcoercionStringDeref了解更多详细信息。

于 2016-05-30T08:49:01.420 回答
7

其他答案是正确的,但我想指出你有一个不需要的to_string(你已经collect编入 a String)和一种强制转换为 a 的替代方式&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));
    }
}

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

于 2016-05-30T14:38:43.740 回答
4

get方法的定义如下所示

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

第一部分是您传递的对象类型:Q. 有限制Q。的条件Q

  1. key-typeK需要实现Borrowtrait overQ
  2. Q需要实现HashEq特征。

用您的实际类型替换它意味着键类型&'static str需要实现Borrow<String>. 根据 的定义Borrow,这意味着 a&'static str需要可转换为&String。但是我读过的所有文档/文本都表明,&String你应该使用的任何地方都应该使用&str。所以提供&str->&String转换是没有意义的,即使它有时会让生活更轻松一些。

由于每个引用类型都可以作为较短生命周期的引用类型来借用&str。),当 a&'static str是键类型时,您可以传递 a ,因为&'static str实现Borrow<str>

于 2016-05-30T09:02:56.427 回答