rustreferenceownership

Move occur because tree has type which does not implement the copy trait, value moved here


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.


Solution

  • 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?