我正在尝试创建一个DefaultHashMap基本上是包装器的结构,HashMap区别在于当获取不在地图中的键时,默认值将被放入该键并返回.
我做了get一个get_mut方法,这很好.现在我正在尝试实现Index并IndexMut围绕这些方法包装.在这里,我遇到了两个问题.
第一个问题是由于get当密钥不存在时必须改变结构的事实,它需要一个可变引用.然而,对于签名index的方法Index有&self代替&mut self,让我无法实现它.
这导致第二个问题,IndexMut需要Index实现.因此即使IndexMut没有实现问题,我也无法做到这一点,因为Index无法实现.
第一个问题很烦人,但可以理解.对于第二个,我不明白为什么要求就在那里.我想有办法解决它.现在我正在做以下事情,但我希望有人有更好的解决方案:
impl<K: Eq + Hash, V: Clone> Index<K> for DefaultHashMap<K, V> {
type Output = V;
fn index(&self, _: K) -> &V {
panic!("DefautHashMap doesn't implement indexing without mutating")
}
}
impl<K: Eq + Hash, V: Clone> IndexMut<K> for DefaultHashMap<K, V> {
#[inline]
fn index_mut(&mut self, index: K) -> &mut V {
self.get_mut(index)
}
}
Run Code Online (Sandbox Code Playgroud)
首先,我怀疑你的要求" 当获得一个不在地图中的密钥时,默认值放在那个密钥中 "并不是完全必需的!
考虑一个不可变的访问let foo = default_hash_map[bar] + 123;.除非你打算在地图中使用具有内部可变性的值,否则无论default_hash_map[bar]是实际创建键还是只返回对单个默认值的引用都可能无关紧要.
现在,如果您确实需要在访问期间创建新条目,那么有一种方法可以做到这一点.借用检查器限制只允许您添加具有可变访问权限的新条目,这是为了阻止您创建悬挂指针,这些指针会在您按住引用的同时修改地图时发生.但是,如果您使用的是具有稳定引用的结构,那么当您在结构中输入新条目时,stable表示引用不会失效,那么借用检查器试图阻止的问题将会消失.
在C++中,我会考虑使用标准保证的deque,以便在向其添加新条目时使其引用无效.不幸的是,Rust deques是不同的(尽管你可能会发现具有类似于C++双端队列的属性的竞技场分配器包),所以我正在使用这个例子Box.盒装值分别驻留在堆上,并且在您添加新条目时不会移动HashMap.
现在,您的正常访问模式可能是修改新条目,然后访问映射的现有条目.因此,创建新条目Index::index是一个例外,不应该减慢地图的其余部分.因此,仅为Index::index访问支付拳击价格可能是有意义的.要做到这一点,我们可能会使用第二个结构,它只保留盒装Index::index值.
知道HashMap<K, Box<V>>可以在不使现有V引用无效的情况下插入,允许我们将其用作临时缓冲区,保存Index::index创建的值,直到我们有机会将它们与主要同步HashMap.
use std::borrow::Borrow;
use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Index;
use std::ops::IndexMut;
struct DefaultHashMap<K, V>(HashMap<K, V>, UnsafeCell<HashMap<K, Box<V>>>, V);
impl<K, V> DefaultHashMap<K, V>
where K: Eq + Hash
{
fn sync(&mut self) {
let buf_map = unsafe { &mut *self.1.get() };
for (k, v) in buf_map.drain() {
self.0.insert(k, *v);
}
}
}
impl<'a, K, V, Q: ?Sized> Index<&'a Q> for DefaultHashMap<K, V>
where K: Eq + Hash + Clone,
K: Borrow<Q>,
K: From<&'a Q>,
Q: Eq + Hash,
V: Clone
{
type Output = V;
fn index(&self, key: &'a Q) -> &V {
if let Some(v) = self.0.get(key) {
v
} else {
let buf_map: &mut HashMap<K, Box<V>> = unsafe { &mut *self.1.get() };
if !buf_map.contains_key(key) {
buf_map.insert(K::from(key), Box::new(self.2.clone()));
}
&*buf_map.get(key).unwrap()
}
}
}
impl<'a, K, V, Q: ?Sized> IndexMut<&'a Q> for DefaultHashMap<K, V>
where K: Eq + Hash + Clone,
K: Borrow<Q>,
K: From<&'a Q>,
Q: Eq + Hash,
V: Clone
{
fn index_mut(&mut self, key: &'a Q) -> &mut V {
self.sync();
if self.0.contains_key(key) {
self.0.get_mut(key).unwrap()
} else {
self.0.insert(K::from(key), self.2.clone());
self.0.get_mut(key).unwrap()
}
}
}
fn main() {
{
let mut dhm = DefaultHashMap::<String, String>(HashMap::new(),
UnsafeCell::new(HashMap::new()),
"bar".into());
for i in 0..10000 {
dhm[&format!("{}", i % 1000)[..]].push('x')
}
println!("{:?}", dhm.0);
}
{
let mut dhm = DefaultHashMap::<String, String>(HashMap::new(),
UnsafeCell::new(HashMap::new()),
"bar".into());
for i in 0..10000 {
let key = format!("{}", i % 1000);
assert!(dhm[&key].len() >= 3);
dhm[&key[..]].push('x');
}
println!("{:?}", dhm.0);
}
{
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
struct K(u32);
impl<'a> From<&'a u32> for K {
fn from(v: &u32) -> K {
K(*v)
}
}
impl<'a> Borrow<u32> for K {
fn borrow(&self) -> &u32 {
&self.0
}
}
let mut dhm = DefaultHashMap::<K, K>(HashMap::new(),
UnsafeCell::new(HashMap::new()),
K::from(&123));
for i in 0..10000 {
let key = i % 1000;
assert!(dhm[&key].0 >= 123);
dhm[&key].0 += 1;
}
println!("{:?}", dhm.0);
}
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
请注意,装箱仅稳定新条目的插入.要删除盒装条目,您仍需要mutable(&mut self)访问权限DefaultHashMap.
| 归档时间: |
|
| 查看次数: |
175 次 |
| 最近记录: |