You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用&str索引HashMap<&String, V>?

如何用&str索引HashMap<&String, V>?

问题重现

你的代码尝试用字符串字面量"x"索引键类型为&String的HashMap,触发了Borrow<str> trait未实现的错误:

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>`

解决方法

1. 自行构建HashMap时:优先修改键类型

如果是你自己创建的HashMap,最推荐的方式是将键类型改为&str——它的兼容性更好,支持用&String、String、字符串字面量等多种类型索引:

use std::collections::HashMap;

fn main() {
    let strings = vec!["x".to_owned(), "y".to_owned(), "z".to_owned()];
    // 键类型改为&str
    let m = [0, 1, 2]
        .into_iter()
        .map(|i| (strings[i].as_str(), i))
        .collect::<HashMap<&str, usize>>();
    let zero = m["x"]; // 正常运行
}

2. 处理他人提供的HashMap<&String, V>

如果无法修改HashMap的键类型,有两种可行方案:

方案A:使用raw_entry API(无额外内存分配)

利用HashMap的raw_entry接口,直接基于字符串的哈希和内容比较查找,不需要创建临时String,性能最优:

use std::collections::HashMap;
use std::hash::{Hash, Hasher};

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 mut hasher = std::collections::hash_map::DefaultHasher::new();
    "x".hash(&mut hasher);
    let hash = hasher.finish();
    
    // 使用raw_entry查找
    if let Some((_, &value)) = m.raw_entry().from_key_hashed_nocheck(hash, |key| key.as_str() == "x") {
        println!("找到值:{}", value); // 输出:找到值:0
    }
}

方案B:临时创建String(简单但有内存开销)

如果对性能要求不高或字符串较短,可以临时创建String并取引用查找,代码更简洁:

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>>();
    
    // 临时创建String并取引用查找
    if let Some(&value) = m.get(&"x".to_string()) {
        println!("找到值:{}", value); // 输出:找到值:0
    }
}

关于T: Borrow<U>时&T: Borrow<U>的疑问

你提到的逻辑不符合Rust中Borrow trait的设计语义:

  • Borrow<U>的核心是类型本身可以借用出一个U,比如String可以借用成str,因此String: Borrow<str>。
  • &String是String的引用,它的Borrow实现仅针对&String本身,而非str。Rust没有为所有&T自动实现Borrow<U>(即使T: Borrow<U>),这是为了避免语义歧义——Borrow要求借用后的类型在哈希、比较逻辑上与原类型一致,而引用类型的语义更偏向“对原类型的指针”,而非原类型的内容。

内容的提问来源于stack exchange,提问作者BallpointBen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 23:44:52