使用`&str`索引`HashMap<&String, V>`

问题描述 投票:0回答:2

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
只是为了引用它似乎很浪费。

rust borrow-checker
2个回答
6
投票

如果您愿意浸入不安全的水域,有办法解决这个问题。

此解决方案利用了以下事实:如果您有一个标记为

S
的结构体
#[repr(transparent)]
并包含单个
T
类型的字段,则可以安全地从
&'a T
转换为
&'b S
(其中
'a: 'b
)。 (例如,这就是为什么您可以从
&Path
借用
PathBuf
,即使实际上不存在
Path
。) 所以,让我们构建一个可以借鉴

String

的新类型,并具有一个积极的自行车棚名称。

#[derive(Hash, PartialEq, Eq)]
#[repr(transparent)]
struct StrRef(str);

这个定义允许我们安全地从 
&str

转换为

&StrRef
,因此我们将把那段不安全的代码包装在
From
实现中:
impl<'a> From<&'a str> for &'a StrRef {
    fn from(value: &'a str) -> &'a StrRef {
        // SAFETY: StrRef is repr(transparent) and contains only a str.
        unsafe { &*(value as *const str as *const StrRef) }
    }
}

现在我们可以在任何可以获得 
Borrow<StrRef>

的类型上实现

&str
// Not actually needed, just for example.
impl Borrow<StrRef> for String {
    fn borrow(&self) -> &StrRef {
        self.as_str().into()
    }
}

// This is the impl you need in this case.
impl Borrow<StrRef> for &String {
    fn borrow(&self) -> &StrRef {
        self.as_str().into()
    }
}

最后,这允许我们编写以下函数,它实现了使用 
&str

从地图中获取数据的目标,而无需将其转换为拥有的

String
fn get_from_map<'a, T>(map: &'a HashMap<&String, T>, key: &str) -> Option<&'a T> {
    map.get(<&StrRef>::from(key))
}

(
游乐场

) 显然,如果您可以将地图的键类型更改为

&str

,那么您应该更喜欢这样做,但是如果您无法控制地图的来源,那么这是一个可行的解决方法(希望是非常可行的)您的计划范围有限)。

    


5
投票
现有

冲突 impl<T> Borrow<T> for T {

所以无法添加。

游乐场

当您处于控制状态时,您不应该使用

&String

作为开始键。首选方法是使用

&str
,它可以轻松地从
&String
创建,并且不会遇到问题,而且更通用。
当您无法控制时,创建 

String

只是为了索引是您唯一安全的选择。

    

© www.soinside.com 2019 - 2024. All rights reserved.