This is an interesting problem that has to do with ownership, memory safety, and performance.
I discussed it with a few folks over IRC, but would like to hear what a broader audience thinks.
Let's call this data structure a BPlusTreeMap<K, V>
, which is going to be similar to BTreeMap<K, V>
.
Obviously, keys must be ordered, so there's our first trait bound: K: Ord
.
I want to support just two operations:
fn insert(&mut self, key: K, value: V)
fn search(&self, key: &K) -> Option<&V>
Now, in a B+ tree leaf nodes contain elements (K, V)
and internal nodes contain copies K
of keys, whereas a B tree has just one (K, V)
for each inserted element (maybe in a leaf node, maybe in an internal node).
Ok, so a B+ tree must be able to copy keys for it's internal use, therefore we have another bound: K: Clone
.
Suppose we want to create a BPlusTreeMap<String, i32>
. There is going to be a ton of string cloning, which will have a horrible effect on performance. However, we don't really need clones of keys. Having unsafe shallow copies of keys in internal nodes would be just fine. So in this example, we could just memcpy those string keys in order to have copies in internal nodes. Then we just have to be careful not to call destructors on those copies. This eliminates the K: Clone
bound.
Ok, this is a good performance improvement. But now we have a memory safety problem!
Suppose that K
is a value which contains an ArcCell<Option<Box<i32>>>
and on every comparison does something with it, perhaps drops the Box
or allocates a new one. If we are sometimes operating on the real K
and sometimes on a shallow copy of it, then they will eventually go out of sync and we might have double frees! This is bad.
So how do we fix this? That's the hard part where I need help
We figured out a possible solution over IRC. It seems that what we really want is to forbid keys which are cell-like. In other words, forbid keys which have interior mutability. We need a bound K: NotCell
, where NotCell
is defined like this:
#![feature(optin_builtin_traits)]
trait NotCell {}
impl NotCell for .. {}
impl<T> !NotCell for std::cell::UnsafeCell<T> {}
I'm very happy with this solution, but would need some reassurance that this is indeed correct.
What do you think? Do you have any better ideas?