michaelsproul / rust_radix_trie

Fast generic radix trie implemented in Rust
https://docs.rs/radix_trie/
MIT License
188 stars 33 forks source link

Add the ability to return a Trie with a root prefix included #18

Closed andrewcsmith closed 8 years ago

michaelsproul commented 8 years ago

Sorry it's been so long with no reply! I was hesitant to merge this because I'm not sure this is the most general solution... but I think it's pretty good and I may as well merge it for now. I'll have to check over it when I have time, but hopefully that shouldn't be too much longer!

andrewcsmith commented 8 years ago

This is one of those things where I'm not even sure how it works anymore. Luckily there's a test!

How could it be more general? I think it probably adds a little generality, maybe not ultimate generality, but then again that bridge can wait for the moment.

michaelsproul commented 8 years ago

How could it be more general? I think it probably adds a little generality, maybe not ultimate generality, but then again that bridge can wait for the moment.

I think there are few issues worth addressing:

// Equivalent to the `Root` structure in your current code, but replacing the current top-level Trie.
struct Trie<K, V> {
  prefix: NibbleVec,
  node: TrieNode<K, V>
}

enum TrieNode<K, V> {
    key: NibbleVec,
    key_value: Option<Box<KeyValue<K, V>>>,
    length: usize,
    child_count: usize,
    children: [Option<Box<TrieNode<K, V>>>; BRANCH_FACTOR]
}

Now the problem with this is that the Root owns the node that it contains, but if we're returning a sub-trie, we might want to return a struct with an allocated NibbleVec and a borrowed trie node (as in the current set-up). One way to do this safely and without duplicating code everywhere might be to parameterise over things that can appear as nodes...

struct Trie<K, V, N> {
  prefix: NibbleVec,
  node: N
}

I dunno, seems like a right mess.

andrewcsmith commented 8 years ago

So, I'm just now returning to the project that uses this as a fairly integral component. Is there any reason we couldn't just do:

struct Trie<'a, K, V> {
    prefix: NibbleVec,
    node: Cow<'a, TrieNode<K, V>>
}

Seems to me like this would allow for returning a Trie that could contain a sub-trie, which would be borrowed initially but could optionally be written to.

michaelsproul commented 8 years ago

@andrewcsmith: Using a Cow looks like a good idea. I'm sorry I really haven't had any time to work on this crate recently, if you'd like to have a crack at implementing the changes we've discussed, I'll give you commit rights to the repo.

andrewcsmith commented 8 years ago

Sounds good. I'm doing some work on tangentially related stuff over the next month, so I'll see if I can work this fix into that.

michaelsproul commented 8 years ago

Hi @andrewcsmith, I have good news! I found some time to work on this, and I think I've solved the subtrie problem! I started off mainly trying to optimise the core get/insert/remove operations, ended up adding a distinct TrieNode struct, and then the SubTrie stuff kind of just fell out, at the cost of slightly more code.

The Traversal traits as they once were are dead, because the Trie now uses iteration for almost all its operations (and tail recursion for remove at the moment).

If you could try out the new subtrie API in your project and provide feedback that would be much appreciated :smile:. You can find it on this branch:

https://github.com/michaelsproul/rust_radix_trie/tree/new-stuff

(and this PR: https://github.com/michaelsproul/rust_radix_trie/pull/21)

The diff isn't particularly useful because of the extensive restructuring of almost everything.

Cheers,

Michael