重新组织struct

时间:2017-05-23 06:21:09

标签: data-structures struct tree rust

我正在尝试在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重新分配给一个新框,并且应该释放前一个框和引用的堆结构。

1 个答案:

答案 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有关。