-1

I tried to implement an add operation in a binary tree:

use std::cell::RefCell;
use std::cmp::PartialOrd;

type Link<T> = RefCell<Option<Box<Node<T>>>>;

struct Node<T> {
    key: T,
    left: Link<T>,
    right: Link<T>,
}

struct Tree<T> {
    root: Link<T>,
}

impl<T> Node<T> {
    fn new(val: T) -> Self {
        Node {
            key: val,
            left: RefCell::new(None),
            right: RefCell::new(None),
        }
    }
}

impl<T: PartialOrd> Tree<T> {
    fn new() -> Self {
        Tree {
            root: RefCell::new(None),
        }
    }

    fn add(&self, val: T) {
        let mut next = self.root.borrow();
        let node = Box::new(Node::new(val));
        match next.as_ref() {
            None => {
                self.root.replace(Some(node));
                ()
            }
            Some(root_ref) => {
                let mut prev = root_ref;
                let mut cur: Option<&Box<Node<T>>> = Some(root_ref);
                while let Some(node_ref) = cur {
                    prev = node_ref;
                    if node.key < node_ref.key {
                        next = node_ref.left.borrow();
                    } else {
                        next = node_ref.right.borrow();
                    }
                    cur = next.as_ref();
                }
                if node.key < prev.key {
                    prev.left.replace(Some(node));
                } else {
                    prev.right.replace(Some(node));
                }
            }
        }
    }
}

fn main() {}

I don't understand why the next variable doesn't live long enough:

error[E0597]: `next` does not live long enough
  --> src/main.rs:36:15
   |
36 |         match next.as_ref() {
   |               ^^^^ borrowed value does not live long enough
...
60 |     }
   |     - `next` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

error[E0597]: `next` does not live long enough
  --> src/main.rs:51:27
   |
51 |                     cur = next.as_ref();
   |                           ^^^^ borrowed value does not live long enough
...
60 |     }
   |     - `next` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

next lives for the entire scope of the add function and, in my opinion, other variables containing references to it are dropped before next has dropped.

The compiler says that "values in a scope are dropped in the opposite order they are created", suggesting that there is another way to declare variables and to solve this problem, but I don't know how.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Is there any particular reason you need to use a `RefCell`? Why not just have `add` take a mutable reference to `self`? – Kwarrtz Aug 06 '18 at 06:39
  • I'm alowed to take only one mutable referece to the entire tree, and I need somehow traverse it to find appropriate node. I need to maintain current node,while traversing, via another mutable reference to a specific node of the tree.If you know how to do this via one mutable reference,please, tell me.It's interesting for me.@Kwarrtz – Игорь Корпенко Aug 06 '18 at 09:46
  • Please review how to create a [MCVE] and then [edit] your question to include it. For example, do you need to have *both* `left` and `right`? Do you need `key`? Do you need separate `Tree` and `Node` structures? There are [Rust-specific MCVE tips](//stackoverflow.com/tags/rust/info) as well. – Shepmaster Aug 06 '18 at 12:39
  • This concept has [been repeatedly covered](https://stackoverflow.com/questions/linked/37986640?lq=1) and is canonically answered by [Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time](https://stackoverflow.com/q/37986640/155423). It is not an innate requirement to track two pointers into the tree in order to add a new value, that's just your implementation. – Shepmaster Aug 06 '18 at 19:08
  • See especially [Adding an append method to a singly linked list](https://stackoverflow.com/q/43976787/155423), [How do I keep a mutable reference to the last node while building a linked list?](https://stackoverflow.com/q/49337968/155423), and [Iterating through a recursive structure using mutable references and returning the last valid reference](https://stackoverflow.com/q/49337968/155423). – Shepmaster Aug 06 '18 at 19:11
  • You might be interested in [this playground I threw together](https://play.rust-lang.org/?gist=a44c6ce14b905fcc509dde85a2f4e653&version=stable&mode=debug&edition=2015) . I had a lot of trouble parsing what your `add` function did exactly, so it's possible my implementation doesn't have exactly the same behavior, but I think it's at least close. – Kwarrtz Aug 07 '18 at 07:04

1 Answers1

1

The problem I see is that in order to update a leaf node of your tree you have to hold a reference to each intermediate step, not only its parent, because all the links up to the root node must be kept alive while you are updating the value. And Rust lifetimes just cannot do that.

That is, Rust cannot do that in a loop, but it can do that in a recursive function, and a tree is best implemented with a recursive function.

Naturally, your recursive struct is Node, not Tree, but something like this could work (there are many ways to get the borrows to work):

impl<T: PartialOrd> Node<T> {
    fn add(&self, val: T) {
        let mut branch = if val < self.key {
            self.left.borrow_mut()
        } else {
            self.right.borrow_mut()
        };
        if let Some(next) = &*branch {
            next.add(val);
            return;
        }
        //Separated from the if let so that branch is not borrowed.
        *branch = Some(Box::new(Node::new(val)));
    }
}

And then, in impl Tree just relay the work to this one.

The code may be simplified a bit if, as other people noted, you get rid of the Tree vs Node and the RefCell...

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • *and a tree is **best** implemented with a recursive function* (emphasis mine) — maybe in the sense of "matches the data structure" or "matches programmer intuition", but not in the sense of "will not run out of stack space on a big tree". – Shepmaster Aug 06 '18 at 18:30
  • @Shepmaster: Sure, when I said _best_ I meant _easiest to write/read_. And deep trees may need more care (I looked at the implementation of `std::collections::BTreeMap` and didn't understand half of it...) But in this particular case, my function is tail recursive except for the `RefMut::drop()` call. Would `rustc` be able to optimize the tail recursion if the OP gets rid of those `RefCell`? – rodrigo Aug 06 '18 at 19:21
  • 1
    *Would rustc be able to optimize the tail recursion* — "maybe". It's up to all sorts of complicated optimizer decisions. There's a reserved keyword `become` to annotate functions as guaranteed tail-recursive, but it's never been implemented. I do seem to recall that `Drop::drop` is a big hurdle. – Shepmaster Aug 06 '18 at 19:26
  • This is what working for my leetcode problem: ``` if let Some(node) = root { let v = node.borrow(); q2.push(v.left.clone()); q2.push(v.right.clone()); } ``` – Charlie 木匠 Jun 12 '20 at 06:36