如何使用仅在条目空缺时构建的昂贵密钥使用Entry API?

Tim*_*ann 8 hashmap rust

是否可以使用EntryAPI通过a获取值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);
}
Run Code Online (Sandbox Code Playgroud)

操场

我的问题是,总是会创建get_or_create一个String遗嘱,导致不必要的内存分配,即使占用的条目不需要它.是否有可能以任何方式解决这个问题?也许以一种整洁的方式Cow

Sta*_*eur 8

在夜间 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);
}
Run Code Online (Sandbox Code Playgroud)

应用于您的原始示例:

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
}
Run Code Online (Sandbox Code Playgroud)