在 Rust 中取消引用字符串和 HashMap
Posted
技术标签:
【中文标题】在 Rust 中取消引用字符串和 HashMap【英文标题】:Dereferencing strings and HashMaps in Rust 【发布时间】:2016-09-28 00:15:27 【问题描述】:我试图了解 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 的 Trait 实现部分将取消引用指定为 fn deref(&self) -> &str
那么这里发生了什么?
【问题讨论】:
我认为在这里使用Borrow
特征是错误的(无论是谁编写了HashMap::get
)。基本上,通用绑定说:您可以将对任何类型的引用传递给get
,如果键类型可作为该类型借用。实际上应该是:您可以将任何类型传递给get
,只要该类型可强制转换为键类型即可。但是我们不能向后兼容地解决这个问题:(
【参考方案1】:
该错误是由于编译器在类型推断期间选择了泛型函数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
但从不会 @987654337 @。而&'static str
没有实现Borrow<String>
。这会导致类型错误。当我们指定HashMap::get::<str>
时,该函数需要&str
,当强制可以应用于&String
以获得匹配的&str
。
您可以查看Deref
coercion 和String
Deref 了解更多详情。
【讨论】:
谢谢。这就说得通了。但是,这不是意味着 Dref 有两个实现吗?一个返回 &str 和另一个 &String?该文档没有提及&String。我在这里错过了什么吗?String
only implements Deref<Target=str>
。此实现可以将&String
强制转换为&str
,其中预计$str
。见Deref
coercion。【参考方案2】:
其他答案是正确的,但我想指出您有一个不需要的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));
注意,地图不需要明确的类型,它会被推断出来。
【讨论】:
只是添加另一个等效的替代方法:println!(":?", roman2number.get(r0.as_str()));
,新的 ::as_str
方法。【参考方案3】:
get
方法的定义如下所示
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq
第一部分是您传递的对象类型:Q
。 Q
有限制。 Q
的条件是
-
键类型
K
需要在Q
上实现Borrow
trait
Q
需要实现 Hash
和 Eq
特征。
用您的实际类型替换它意味着键类型&'static str
需要实现Borrow<String>
。根据Borrow
的定义,这意味着&'static str
需要可转换为&String
。但是我读过的所有文档/文本都表明,无论您在哪里使用&String
,您都应该使用&str
。因此,提供&str
-> &String
转换毫无意义,即使有时它会让生活变得更轻松。
由于每个reference type is borrowable as a shorter lived reference type.),当&'static str
是键类型时,您可以传递&str
,因为&'static str
实现Borrow<str>
【讨论】:
谢谢。 Borrow 的解释很有道理!以上是关于在 Rust 中取消引用字符串和 HashMap的主要内容,如果未能解决你的问题,请参考以下文章