2016-05-30 92 views
9

我想了解HashMap如何在Rust中工作,并且我已经提出了这个例子。提取Rust中的字符串和HashMaps

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

那么,什么是解引用在这里发生?

+0

我认为这是错误的(任何人创作'HashMap :: get')在这里使用'借用'特征。基本上,泛型边界表示:如果键类型可以作为该类型借用,则可以将引用传递给“get”。它实际上应该是:只要该类型对于键类型是可强制的,就可以将任何类型传递给'get'。但是,我们无法解决这个问题: –

回答

7

错误是由编译器在类型推断过程中选择的通用函数HashMap::get而不是String引起的。但你想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()作为&String类型,但从来没有&str。并且&'static str不执行Borrow<String>。这会导致类型错误。当我们指定HashMap::get::<str>时,此功能需要&str,当强制可应用于&String以获得匹配&str时。

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

+0

谢谢,这很有道理,但是,这是不是意味着Dref有两个实现?一个返回&str和其他&String?文档没有提及&String。 – skanur

+0

['String'只实现'Deref '](https://doc.rust-lang.org/nightly/std/string/struct.String.html#deref)。这个实现使得它有可能将'&str'强制转换为'&str',其中需要'$ str'。参见['Deref'强制转换](https://doc.rust-lang.org/nightly/book/deref-coercions.html)。 – WiSaGaN

1

get方法的定义如下所示

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

第一部分是其中传递对象的类型:QQ有限制。在Q的条件是

  1. 钥匙式K需要实现Borrow特质在Q
  2. Q,需要实现HashEq特质。

将此替换为您的实际类型意味着密钥类型&'static str需要实施Borrow<String>。按照Borrow的定义,这意味着&'static str需要转换为&String。但是,我读过的所有文档/文本都表明,无论您使用的是哪种文件,&String都应该使用&str。所以提供&str - >&String转换是没有意义的,即使它有时会让生活变得更轻松。

由于每个reference type is borrowable as a shorter lived reference type),你可以通过一个&str&'static str是关键型的,因为&'static str实现Borrow<str>

+0

谢谢。对于借用的解释是有道理的! – skanur

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

注意有没有必要有地图明确的类型,它会被推断。

+0

并且为了添加另一个相同的选择:'println!(“{:?}”,roman2number.get(r0.as_str()));',new-ish [': :as_str'](https://doc.rust-lang.org/collections/string/struct.String.html#method.as_str)方法。 – c0g