-1

I know it's a redundant question with rust but it seems I can not manage to get some generality from it. So I attempted to write a simple binary tree using some code from elsewhere (at first I used a Box of options, but it seems options of box are the way to go).

#[derive(Clone)]
pub struct BinaryTree<T>
where T:Clone
{
    pub val: T,
    pub left : Option<Box<BinaryTree<T>>>,
    pub right : Option<Box<BinaryTree<T>>>,
}

impl<T: std::clone::Clone> BinaryTree<T>
{
    pub fn new(val:T) -> Self
    {
        BinaryTree
        {
            val,
            left:None,
            right:None,
        }
    }

    pub fn insertleft( mut self, node: BinaryTree<T>)-> Self
    {
        //let old_left = mem::take(&mut self.left);
        let left_option = Some(Box::new(node ));
        self.left = left_option;
        self
    }

    pub fn insertright( mut self, node: BinaryTree<T>)-> Self
    {
        let right_option = Some(Box::new( node ));
        self.right = right_option;
        self
    }

}

So here is my problem. If I create a tree like that:

let tree = BinaryTree::new(1).insertleft(BinaryTree::new(2)).insertright(BinaryTree::new(3));

There is actually no problem and I can access the left value, the right value without any problem. But if I try to fill the tree step by step

let tree = BinaryTree::new(1);
tree.insertleft(BinaryTree::new(2));
tree.insertright(BinaryTree::new(3));

then I run into errors like "use of moved value: tree.left" whether I declare tree mutable or not. I do not really get where or how the tree is supposed to be "consumed" here. If I use & mut ref instead of ref in the insertleft and insertright functions I run into similar errors such as can not move out of which is behind a mutable reference. I read plenty of topics about such problems but I really do not get why you can not access a public field by what is in the end, more or less a setter.

1 Answer 1

1

Your insertleft and insertright functions take self, which tranfers ownership of the BinaryTree to those functions. They then return it, which you currently discard.

If you want to construct the tree step by step, you can store those return values in new variables to be used for the next step of construction:

let tree = BinaryTree::new(1);
let tree1 = tree.insertleft(BinaryTree::new(2));
let tree2 = tree1.insertright(BinaryTree::new(3));

Alternatively, if you don't need to chain construction and insertions, you can take &mut reference to self:

impl<T: std::clone::Clone> BinaryTree<T> {
    pub fn insertleft(&mut self, node: BinaryTree<T>) -> &mut Self {
        let left_option = Some(Box::new(node));
        self.left = left_option;
        self
    }

    pub fn insertright(&mut self, node: BinaryTree<T>) -> &mut Self {
        let right_option = Some(Box::new(node));
        self.right = right_option;
        self
    }
}

fn main() {
    let mut tree = BinaryTree::new(1);
    tree.insertleft(BinaryTree::new(2));
    tree.insertright(BinaryTree::new(3));

    // Due to returning &mut Self, this also works:
    let mut tree = BinaryTree::new(1);
    tree.insertleft(BinaryTree::new(2))
        .insertright(BinaryTree::new(3));
}

This reference allows insertleft and insertright to modify tree in place, keeping ownership of the BinaryTree in main. However, you can no longer chain construction and insertion because BinaryTree::new(1).insertleft(BinaryTree::new(2)).insertright(BinaryTree::new(3)) would yield a reference which is less flexible than an owned value.

For more information, see https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html and Is there a difference between using a reference, and using an owned value in Rust?

Sign up to request clarification or add additional context in comments.

5 Comments

May be should you illustrate the «chain» construction as equivalent to: let tree1=tree.insertleft...; let tree2=tree1.insertright...; in order to artificially name the temporaries as an illustration of what happens.
Ah ok, thanks, that's perfect, in the second case, my mistake was to return self then, which was obviously problematic now that you say it. The first solution is a bit hacky but it fits me well, I tried to use new variables instead of overshadowing, and it didn't work I ran into the same problems.
Note that you can chain methods that take mut self: all you have to do is to return self from the method, which can then be used for chaining or ignored if you want a pure step-by-step construction: playground
@Jmb correct me if I'm wrong, but the chaining approach in your Playground link produces a &mut reference which cannot be, for example, returned.
@FinnBear yes and no: if you want to return it, you need to store the new tree in a local variable and return that (playground), which is slightly less ergonomic than the pure "transfer of ownership" solution but no worse than the borrowing solution. And OTOH it may be more ergonomic than the borrowing solution when moving is not needed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.