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)
)
}
);
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.
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)
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
});
1 Like
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.
1 Like
They were right about friendly rustaceans community, thank you for the time dedicated!
5 Likes