如何用&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
相关产品推荐
相关产品推荐

