如何将 Entry API 与仅在 Entry 空置时构建的昂贵密钥一起使用?
Posted
技术标签:
【中文标题】如何将 Entry API 与仅在 Entry 空置时构建的昂贵密钥一起使用?【英文标题】:How do I use the Entry API with an expensive key that is only constructed if the Entry is Vacant? 【发布时间】:2019-01-03 15:18:50 【问题描述】:是否可以使用Entry
API 通过AsRef<str>
获取值,但使用Into<String>
插入它?
这是工作示例:
use std::collections::hash_map::Entry, HashMap;
struct Foo;
#[derive(Default)]
struct Map
map: HashMap<String, Foo>,
impl Map
fn get(&self, key: impl AsRef<str>) -> &Foo
self.map.get(key.as_ref()).unwrap()
fn create(&mut self, key: impl Into<String>) -> &mut Foo
match self.map.entry(key.into())
Entry::Vacant(entry) => entry.insert(Foo ),
_ => panic!(),
fn get_or_create(&mut self, key: impl Into<String>) -> &mut Foo
match self.map.entry(key.into())
Entry::Vacant(entry) => entry.insert(Foo ),
Entry::Occupied(entry) => entry.into_mut(),
fn main()
let mut map = Map::default();
map.get_or_create("bar");
map.get_or_create("bar");
assert_eq!(map.map.len(), 1);
playground
我的问题是在get_or_create
中总是会创建一个String
,这会导致不需要的内存分配,即使占用的条目不需要它也是如此。有可能以任何方式解决这个问题吗?也许用Cow
的简洁方式?
【问题讨论】:
【参考方案1】:在 nightly Rust 中,您可以使用不稳定的raw_entry_mut()
功能,它允许这样做:
为 HashMap 创建一个原始条目构建器。
[...]
原始条目对于以下特殊情况很有用:
将拥有的密钥的创建推迟到已知需要时
在稳定的 Rust 中,您可以添加具有相同 API 但稳定的 hashbrown crate。 hashbrown crate 实际上是标准库的 hashmap 的底层实现。
例子:
#![feature(hash_raw_entry)]
use std::collections::HashMap;
#[derive(Hash, PartialEq, Eq, Debug)]
struct NoCopy
foo: i32,
impl Clone for NoCopy
fn clone(&self) -> Self
println!("Clone of: :?", self);
Self foo: self.foo
fn main()
let no_copy = NoCopy foo: 21 ;
let mut map = HashMap::new();
map.raw_entry_mut()
.from_key(&no_copy)
.or_insert_with(|| (no_copy.clone(), 42));
map.raw_entry_mut()
.from_key(&no_copy)
.or_insert_with(|| (no_copy.clone(), 84));
println!(":#?", map);
应用于您的原始示例:
fn get_or_create<K>(&mut self, key: K) -> &mut Foo
where
K: AsRef<str> + Into<String>,
self.map
.raw_entry_mut()
.from_key(key.as_ref())
.or_insert_with(|| (key.into(), Foo))
.1
【讨论】:
【参考方案2】:你不能,安全地。这是当前入口API的一个限制,没有很好的解决方案。预期的解决方案是“原始”入口 API。见Stargateur's answer for an example of using it。
使用 Entry API 的唯一稳定解决方案是始终克隆密钥:
map.entry(key.clone()).or_insert(some_value);
在 Entry API 之外,您可以检查地图是否包含值,如果没有则插入:
if !map.contains_key(&key)
map.insert(key.clone(), some_value);
map.get(&key).expect("This is impossible as we just inserted a value");
另见:
[Pre-RFC] Abandonning Morals In The Name Of Performance: The Raw Entry API WIP: add raw_entry API to HashMap (50821) Extend entry API to work on borrowed keys. (1769) Add HashMap.entry_or_clone() method (1203)对于非基于entry
的解决方案,请参阅:
【讨论】:
以上是关于如何将 Entry API 与仅在 Entry 空置时构建的昂贵密钥一起使用?的主要内容,如果未能解决你的问题,请参考以下文章