Move out of borrowed temporary


#1

Coming from C++, I cannot understand why I cannot move out of a borrowed content while the content is in a smaller scope.
Please consider the following code:

pub struct Tree<T> {
    pub root: Option<Box<Node<T>>>,
    size: usize,
}
impl<T> From<Node<T>> for Tree<T> {
    fn from(node: Node<T>) -> Self {
        Self {
            root: Some(Box::from(node)),
            size: 1,
        }
    }
}


pub struct Node<T> {
    value: T,
    parent: *mut Node<T>,
    left: Option<Box<Node<T>>>,
    right: Option<Box<Node<T>>>,
}
impl<T> Node<T> {
    pub fn set(&mut self, mut left: Node<T>, mut right: Node<T>) -> &mut Self {
        self.left = Some(Box::new(node));
        self.right = Some(Box::new(node));
        self
    }
}
impl<T> From<T> for Node<T> {
    fn from(value: T) -> Self {
        Self {
            value: value,
            parent: std::ptr::null_mut(),
            left: None,
            right: None,
        }
    }
}

Now what I want to do is create a tree:

let n = Node::from;
let tree: Tree<i32> = Tree::from(
    *n(2).set(
        n(3),
        n(4)
    )
);

But this is not possible since:

error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:11:9
   |
11 | /         *n(2).set(
12 | |             n(3),
13 | |             n(4)
14 | |         )
   | |_________^ cannot move out of borrowed content

Then I tried to introduce a scope so that temporaries will be no longer considere, it didn’t work

let tree: Tree<i32> = Tree::from(
    *{
        n(2).set(
            n(3),
            n(4)
        )
    }
);

#2

The set() returns a &mut Self, which you then try to deref back to the value via *n(2) - that’s needed because Tree::from expects a Node value and not a reference to one. However, the returned &mut Self cannot be dereferenced because that would cause a move while it’s still borrowed.

I think your set can be written as:

fn set(mut self, ...) -> Self {...}

Then you you’re just moving the value in and out of the set method, and finally feeding the value to the Tree:from - all that works fine.


#3

Ok, it works but now when I want to call set on some Node, I have to assign its result to something in order to not lose the Node I called the method on. Isn’t there a more convenient solution?

Also, why it does not work when the whole n(2) is called inside a smaller scope (like the last code snippet example)


#4

You can avoid temporaries. With your previous code the following is fine:

let n = Node::from;
let mut tmp = n(2);
tmp.set(n(3),n(4));
let tree: Tree<i32> = Tree::from(tmp);

Or a slightly different formulation of the same thing:

let tree: Tree<i32> = Tree::from({
        let mut tmp = n(2);
        tmp.set(n(3), n(4));
        tmp
    });

#5

I just noticed this part of your post (perhaps in an edit after I read it the first time).

As far as the compiler is concerned, tmp.set(...) extends the borrow of the tmp because you’re returning a &mut Self. While that mutable reference is live, tmp is considered (mutably) borrowed. So your last snippet was trying to return a &mut Self from a block, and then deref that to move the value. But as far as the compiler is concerned, you’re trying to deref a non-Copy value while only having a mutable borrow of it (aka trying to move it).

Although that would be fine, conceptually, in this particular example (because this is just a slightly obfuscated way to return the value (i.e. the temp) out of the block), it’s not how the borrow checker looks at it.


#6

They were right about friendly rustaceans community, thank you for the time dedicated!