我该如何在将键插入HashMap后保留对其的引用?

19
我想向HashMap中插入内容,但是想要保持关键字的不可变借用,以便在其他地方使用。在我的情况下,这些关键字是字符串。
以下是一种方法:
use std::collections::HashMap;
let mut map = HashMap::new();
let id = "data".to_string();  // This needs to be a String
let cloned = id.clone();

map.insert(id, 5);

let one = map.get(&cloned);
let two = map.get("data");
println!("{:?}", (one, two));

但这需要一个克隆。

在 Rust 1.2.0 之前,这个可行:

use std::collections::HashMap;
use std::rc::Rc;
use std::string::as_string;

let mut map = HashMap::new();
let data = Rc::new("data".to_string()); // This needs to be a String
let copy = data.clone();
map.insert(data, 5);

let one = map.get(&copy);
let two = map.get(&*as_string("data"));
println!("{:?}", (one, two));

如何在Rust 1.2.0中实现这个?

理想情况下,我想把一个键放进HashMap里,但仍然保留对它的引用,并允许我使用&str类型的元素访问它,而不需要额外分配内存。


相关问题:https://github.com/rust-lang/rust/issues/60896 - Boiethios
1个回答

17

简短的回答是无法做到。当您将某些内容插入到 HashMap 中时,会转移所有权。这将使您对键具有的任何引用无效,因为该键已经移动到映射分配的内存中。

RFC 1194 (Set Recovery) 提出了一种方法来获取存储在 HashSet 中的键的引用(不是 map)。需要进一步的信息和研究来证明支持 HashMap 也是合理的。然而,这仍然无法帮助您,因为您需要知道键(或可用于查找键的内容)才能再次查找它。但是,在此时您已经将键放入集合中了。

您的第二个解决方案有效,因为您不实际将 String 的所有权交给了 map,而是将其模拟通过引用计数共享所有权的类型的所有权。 clone 调用只是增加了引用计数,这就是许多动态语言解决此问题的方式。

use std::collections::HashMap;
use std::rc::Rc;

fn main() {
    let mut map = HashMap::new();
    let data = Rc::new("data".to_string());
    map.insert(data.clone(), 5);

    let v = map.get(&data);
    println!("{:?}", v);
}

一些不稳定的特性可能会对你有所帮助。其中最有效的是HashMap::raw_entry_mut

#![feature(hash_raw_entry)]

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    let id = "data";

    let (k, _v) = map
        .raw_entry_mut()
        .from_key(id)
        .or_insert_with(|| (String::from(id), 0));

    println!("{:?}", k);
}

一种更短但略微不那么高效的解决方案使用Entry::insert

#![feature(entry_insert)]

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    let id = "data";

    let entry = map.entry(String::from(id)).insert(0);
    let k = entry.key();

    println!("{:?}", k);
}

另请参阅:


这个方案很棒!虽然我不能再使用 &str 类型访问我的映射数据了,但这是必要的。:/ - Michael Eden
这个RFC看起来就是我需要的,可惜它没有考虑HashMaps。 - Michael Eden
3
在技术上,增加一个 fn insert_and_get(&mut self, value: T) -> &T 并返回新插入数据的引用是否会很困难或不可能? - Boiethios
@FrenchBoiethios 我不认为这会很难或不可能。我自己也没有尝试过 :-) - Shepmaster
Entry::insert(从未稳定)已被移除并替换为 Entry::insert_entry(也尚未稳定)。 - kbolino
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接