Generating BST from preorder

Hello there! I'm trying to create a binary search tree from a C implementation. The problem is I can't pass the borrow checker :sweat_smile:

void BST::createFromPreorder(int *pre, int n) {
    // Create root node
    int i = 0;
    root = new Node;
    root->data = pre[i++];
    root->lchild = nullptr;
    root->rchild = nullptr;
 
    // Iterative steps
    Node* t;
    Node* p = root;
    stack<Node*> stk;
 
    while (i < n){
        // Left child case
        if (pre[i] < p->data){
            t = new Node;
            t->data = pre[i++];
            t->lchild = nullptr;
            t->rchild = nullptr;
            p->lchild = t;
            stk.push(p);
            p = t;
        } else {
            // Right child cases
            if (pre[i] > p->data && pre[i] < stk.empty() ? 32767 : stk.top()->data){
                t = new Node;
                t->data = pre[i++];
                t->lchild = nullptr;
                t->rchild = nullptr;
                p->rchild = t;
                p = t;
            } else {
                p = stk.top();
                stk.pop();
            }
        }
    }
}

My failed attempt:

 fn create_preorder(&mut self, items: &[T]) {
        let mut i = 0;
        let n = items.len();
        if n == 0 {
            return;
        }

        self.root = Some(Box::new(Node::new(items[i])));
        i = i + 1;

        let mut stack = VecDeque::new();
        let mut pointer = self.root;

        while i < n {
            let current = items[i];

            if let Some(ref mut node) = pointer {
                if current < node.value {
                    let t = Box::new(Node::new(current));
                    node.left = Some(t);

                    stack.push_back(node);

                    pointer = node.left;
                    i = i + 1;
                } else {
                    if let Some(ref mut parent) = stack.pop_back() {
                        if current > node.value && current < parent.value {
                            let t = Box::new(Node::new(current));
                            node.right = Some(t);

                            pointer = Some(t);
                            i = i + 1;

                            stack.push_back(parent);
                        } else {
                            pointer = Some(parent);
                        }
                    } else if current > node.value {
                        let t = Box::new(Node::new(current));
                        node.right = Some(t);

                        pointer = t;
                        i = i + 1;
                    }
                }
            }
        }
    }

Am I struggling because I'm trying to implement a 1:1 copy from a different language? How it would look like from a Rust perspective?

Thank you!

It is hard to say what might be wrong without details. Show us your type definitions (for Node and whatever self is, and anything else that makes up your data structure) and the full error report (as printed by cargo check, not a screenshot of your IDE).

It's very likely that your stack and pointer will be problematic, but we need to see where you're starting from — and, preferably, have a compilable copy of your code to experiment with — to give good advice on what to do about it.

4 Likes

As @kpreid said, to get help you need to post something that compiles, and also post the full error you're getting when running cargo build at the command line. Otherwise we're just guessing, which isn't very productive. To save time reposting as changes are made during the discussion, post your code to the Rust playground, but note this is only possible if you're using crates that are available there, which is many but not all of them.

But I do see that you're using an algorithm that uses pointers and, when translated directly to Rust, will end up with multiple mutable references to the same item. This is fundamentally not allowed in Rust, since Rust guarantees safety by ensuring that each value has at most one exclusive/mutable reference. And when there is an active mutable reference, then no shared/immutable references are allowed.

If this idea isn't already ingrained in your mind, please go through the the book (or other beginning materials). This should be done before trying other projects. You are correct that it often doesn't work to try to use an algorithm from another language in Rust. See How not to learn Rust.


All that said, I tried to convert your code to use indexes instead of pointers, using the same algorithm. A nice benefit is that there is a single allocation for the BST rather than one per node. I just translated the code and didn't test it, and I may have made mistakes. So please look it over and maybe you can add tests. Feel free to ask any questions you have about the approach. Here's the playground (EDIT: fix initialization of the root, rename get to value and return a ref).

One thing to notice is that, whenever a modification needs to be made to the BST, a method with a &mut self is called, and this is the key to avoiding borrow checker errors. It is tempting instead to get a &mut reference in a local variable, and modify the BST through that variable. But this will result in having multiple &mut references to the BST, or in a single &mut reference along with other non-mut & references, which will result in compiler errors. These are probably the same sort of errors you were getting when you tried this using references.

3 Likes

I really appreciate the time you took to create an implementation using indexes instead of pointers. I was able to debug it locally and it served as a great learning resource for me!

I read the Rust book a while ago, I feel like I should read it again to refresh my understanding of the fundamentals.

I think I may have made the first six mistakes listed in the "How not to learn Rust" guide. :sweat_smile:

In fact, I tried to expand your example to delete a node but I clearly don't have the fundamentals to do it right now, I'll come back later!

Thanks again!

1 Like

You're welcome, glad it helped!

Ha ha, don't worry, they're very common mistakes. It is natural to try to learn Rust the same way you've learned other languages.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.