我正在尝试在Rust中实现二进制搜索树的平衡(AVL)版本的旋转代码,但是我无法声明要重组的节点的所有权。
我的结构:
struct Tree<T> {
root: Box<TreeNode<T>>,
depth: usize,
}
enum TreeNode<T> {
Empty,
Node {
val: T,
left: Tree<T>,
right: Tree<T>,
},
}
我知道我只能使用一种类型,或Option
。这看起来好一点。
当我想实现轮换时:
T1, T2, T3 and T4 are subtrees.
z y
/ \ / \
y T4 Right Rotate (z) x z
/ \ - - - - - - - - -> / \ / \
x T3 T1 T2 T3 T4
/ \
T1 T2
我找不到重新分配节点的方法。我在rotate(&mut self, ...)
节点(z
节点)上调用了Tree<T>
方法,但我需要使用match *self.root {}
将根TreeNode
转换为它的Node
版本来获取组件。这有效,但我无法使用这些提取的值来创建新节点。
如果我试试这个:
fn insert(&mut self, ...) {
...
// need to rotate
rotate(self, ...);
}
fn rotate(&mut ztree, ...) {
ztree.root = match *ztree.root {
// just re assign same tree to test...
TreeNode::Node {val, left, right} =>
Box::new(TreeNode::Node {val: val, left: left, right: right}),
_ => panic!("meh"),
} ...
我收到此错误。
|
171 | ztree.root = match *ztree.root {
| ^^^^^ cannot move out of borrowed content
172 | TreeNode::Node {val, left, right} =>
| --- ---- ----- ...and here (use `ref right` or `ref mut right`)
| | |
| | ...and here (use `ref left` or `ref mut left`)
| hint: to prevent move, use `ref val` or `ref mut val`
我知道它不喜欢我获得盒装TreeNode
的所有权,但我不知道如何告诉Rust我将分配一个新的盒装TreeNode
和旧的TreeNode
可以在当地声明。
如果我尝试self.root = Box::new(TreeNode::Empty)
工作正常,因为它知道我将self.root
重新分配给一个新框,并且应该释放前一个框和引用的堆结构。
答案 0 :(得分:3)
假设Rust 确实信任您替换ztree.root
的值。然后你可以写
fn rotate(&mut ztree, ...) {
let locally_owned = ztree.root (and I promise to give it back);
// Now, ztree.root is in some undefined state.
// Thats OK though, because I promise to fix it before anyone looks!
let local_new_value = match locally_owned {
// just re assign same tree to test...
TreeNode::Node {val, left, right} =>
Box::new(TreeNode::Node {val: val, left: left, right: right}),
// Looks safe-ish, because the whole program will crash,
// so the programmer might expect that no one
// will see the undefined value of ztree.root
// (in fact, there would be problems when the destructor
// of ztree.root is called in the panic)
_ => panic!("meh"),
}
// FIXED IT! Put a value back into ztree.root
ztree.root = local_new_value;
}
看起来很好。但是,想象一下,如果用一些return语句替换panic("meh")
。然后你可以得到这样的代码:
ztree.root = Box::new(TreeNode::Empty);
// Returns without replacing the value of ztree.root
// because ztree.root is Empty
rotate(&mut ztree);
// Now ztree.root is in a undefined state
rotate(&mut ztree); // So something bad happens here
基本上,编译器必须说服自己,不仅要更换ztree.root
的值,而且没有代码路径会导致值被替换。这是复杂的方法,因此,没有办法告诉编译器让你做你想做的事情。
相反,您可以通过重新设置来解决问题。您无需尝试计算新值来替换旧值,而只需更改当前值,而无需替换它。一种方法是使用std::mem::swap
之类的(Playground):
fn rotate<T>(ztree: &mut Tree<T>) {
let ref mut root : TreeNode<T> = *ztree.root;
match root {
&mut TreeNode::Node {ref mut left, ref mut right, ..} => {
std::mem::swap(left, right);
},
_ => unimplemented!(),
}
}
如果您想知道为什么let ref mut root: TreeNode<T> = *ztree.root;
有效,但match *ztree.root {...}
没有,我不太确定,但可能与this issue有关。