HashMap<&String, V>
使用索引的正确方法是什么&str
?Rust 报告&String
不是Borrow<str>
,这是索引所必需的。(这对我来说似乎有点傻;如果T: Borrow<U>
那么肯定&T: Borrow<U>
也应该成立?)
use std::collections::HashMap;
fn main() {
let strings = vec!["x".to_owned(), "y".to_owned(), "z".to_owned()];
let m = [0, 1, 2]
.into_iter()
.map(|i| (&strings[i], i))
.collect::<HashMap<&String, usize>>();
let zero = m["x"];
}
error[E0277]: the trait bound `&String: Borrow<str>` is not satisfied
--> src/main.rs:9:18
|
9 | let zero = m["x"];
| ^^^ the trait `Borrow<str>` is not implemented for `&String`, which is required by `HashMap<&String, usize>: Index<&_>`
|
= help: the trait `Borrow<str>` is implemented for `String`
= help: for that trait implementation, expected `String`, found `&String`
= note: required for `HashMap<&String, usize>` to implement `Index<&str>`
显然,因为我构建了这个特定的 HashMap,所以我可以返回并将其键类型更改为&str
。但是,假设我得到了一个HashMap<&String, V>
,那么对其进行索引的正确方法是什么?构建一个整体String
只是为了引用它似乎很浪费。
如果您愿意涉足不安全的水域,那么就有一种方法可以解决这个问题。
此解决方案利用了这样一个事实:如果您有一个
S
标记的结构#[repr(transparent)]
并且包含单个类型的字段T
,则可以安全地从 转换为&'a T
。(这就是为什么您可以从 a借用 a,即使实际上不存在。)&'b S
'a: 'b
&Path
PathBuf
Path
因此,让我们构建一个可以借鉴的新类型
String
,并为其取一个积极的名字。这个定义允许我们安全地从 转换为
&str
,&StrRef
因此我们将在实现中包装这段不安全的代码From
:现在我们可以
Borrow<StrRef>
对任何能够获得 的类型进行实现&str
。最后,这使我们能够编写以下函数,该函数实现了使用 从地图中获取
&str
而不将其转换为 owned的目标String
:(游乐场)
显然,如果您可以将地图的键类型更改为,
&str
那么您应该更喜欢这样,但如果您无法控制地图的来源,那么这是一个可行的解决方法(希望在您的程序内非常有限的范围内)。与现有
所以无法添加。
操场
当你掌控一切时,你不应该
&String
一开始就使用 作为键。首选方法是使用&str
,它可以轻松地从 中创建&String
,这样就不会出现问题,而且更通用。当您无法控制时,创建索引
String
是唯一安全的选择。