Hi everyone,
I am searching for a type similar to a HashMap<K, V>
that is statically guaranteed to contain a V
for every possible value of K
, i.e. every possible key must have a value. I first thought about creating a data structure like this:
use std::collections::HashMap;
use std::hash::Hash;
#[derive(Debug, Clone)]
struct FullMap<K, V, F = fn(K) -> V>
where
F: Fn(K) -> V,
{
map: HashMap<K, V>,
fallback: F,
}
impl<K, V, F> FullMap<K, V, F>
where
F: Fn(K) -> V,
{
fn from_fn(f: F) -> Self {
Self {
fallback: f,
map: HashMap::new(),
}
}
fn insert(&mut self, key: K, value: V) -> Option<V>
where
K: Hash + Eq,
{
self.map.insert(key, value)
}
fn get(&mut self, key: K) -> &V
where
K: Hash + Eq + Clone,
{
self.map
.entry(key.clone())
.or_insert_with(|| (self.fallback)(key))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[allow(dead_code)]
enum Foo {
A,
B,
C(Bar),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[allow(dead_code)]
enum Bar {
A,
B(bool),
C,
}
fn main() {
let mut my_map: FullMap<Foo, Bar> = FullMap::from_fn(|foo| match foo {
Foo::A => Bar::A,
Foo::B => Bar::B(true),
Foo::C(bar) => bar,
});
assert_eq!(my_map.get(Foo::B), &Bar::B(true));
my_map.insert(Foo::B, Bar::B(false));
assert_eq!(my_map.get(Foo::B), &Bar::B(false));
assert_eq!(my_map.get(Foo::C(Bar::A)), &Bar::A);
}
However, this approach has some drawbacks:
- The
FullMap
is not serializable. - It has three generic parameters (I could just use function pointers, but that would make any immutable capture impossible)
-
get()
needs mutable access in order to ensure consistency (if the function was impure), which means you can't read anything out of a&FullMap
. Maybe interior mutability could be used to resolve this issue.
Is there a better way to achieve my goals of a statically proven always-full HashMap? Are there other problems with the current design?