搜索BST节点的后继者,"克隆以满足借用检查器"灾害

时间:2016-08-22 06:02:05

标签: rust

我正在尝试在Rust中实现BST。我的结构看起来像这样:

pub struct Node<T> {  
    key: T,  
    parent: Option<Box<Node<T>>>,  
    left: Option<Box<Node<T>>>,  
    right: Option<Box<Node<T>>>,  
}  

我正在研究一种查找当前节点后继的方法。经过与借用检查员的长时间斗争,我使它成功,但它现在看起来像这样:

//If right node exists - successor is a min node in it.
//Else - go up a parent node. If parent is None, no successor.
//If origin was parent's left node - parent is a successor.
//Else - go up another level.
pub fn succ(&self) -> Option<Box<Node<T>>> {

    match self.right {
        Some(ref node) => Some(node.min()),
        None => {
            let mut origin = Box::new(self.clone()); //To match types
            let mut parent = origin.parent.clone(); //`Node<T>` not a copy type

            loop {
                let parent_node = match parent.clone() {
                    Some(node) => node,
                    None => break,
                };
                let right_of_parent = match parent_node.clone().right {
                    Some(node) => node,
                    None => break,
                };
                if *origin != *right_of_parent {
                    break;
                }

                origin = parent_node;
                parent = origin.parent.clone();
            }
            parent
        }
    }
}  

如果我删除所有.clone(),编译器就开始用&#34;部分移动值&#34;并且&#34;因为借来的而无法分配&#34;错误。有没有办法让这些代码更具惯用性而不是克隆地狱呢?

UPD:
想发布我最终的解决方案。
首先,上面的代码不起作用,因为父字段不包含引用,而是包含父节点的副本。所以最后问题转变为&#34;如何实现对父节点的引用&#34;。
我考虑了下面的答案,一些booksrelevant answers,最后我得出的结论是,对于我甚至不打算发布的玩具项目来说,它是不值得的。线上。我发现不是最有效但更简单的解决方案 - 根本不使用父母参考 我从上面的结构中删除了父字段,并创建了另一个结构:

pub struct Tree<T> {
    root: Option<Box<Node<T>>>,
}

现在我从树的根部搜索父级。我的succ函数现在看起来像这样:

fn succ<'a>(&'a self, node: &'a Node<T>) -> Option<&Node<T>> {
    match node.right {
        Some(ref rnode) => rnode.min(),
        None => {
            let mut succ = None;
            let mut root = self.root.as_ref();
            loop {
                root = match root {
                    Some(ref rootnode) => {
                        match node.key.cmp(&rootnode.key) {
                            Ordering::Less => {
                                succ = Some(&***rootnode);
                                rootnode.left.as_ref()
                            }
                            Ordering::Greater => rootnode.right.as_ref(),
                            Ordering::Equal => break,
                        }
                    }
                    None => break,
                }
            }
            succ
        }
    }
}

1 个答案:

答案 0 :(得分:4)

欢迎使用Rust和Stack Overflow!

这里的主要问题是Node定义:

pub struct Node<T> {  
    key: T,  
    parent: Option<Box<Node<T>>>,  
    left: Option<Box<Node<T>>>,  
    right: Option<Box<Node<T>>>,  
}

在Rust中,Box<T> 拥有值而不是可以别名的指针。你不可能创造任何非平凡的树木。

您可以尝试将引用计为Box,而不是Rc<T>。您可以对父链接使用Weak指针,以避免它们保持活动状态:

use std::rc::{Rc,Weak};
pub struct Node<T> {  
    key: T,  
    parent: Option<Weak<Node<T>>>,  
    left: Option<Rc<Node<T>>>,  
    right: Option<Rc<Node<T>>>,  
}

一旦对其进行排序,您就不会使用引用。每次你做的事情都是这样的:

let mut parent = origin.parent; //.clone();

您的版本origin.parent的类型为Option<Box<Node<T>>>,您尝试移动 Option字段origin - 因此你必须添加clone()(它克隆Box内的节点,而不仅仅是指针!)。但是,你并不想搬出去;你只想要一个参考,比如:

let parent = &origin.parent;

或者同时进行None检查:

match origin.parent {
    Some(ref parent_ptr) => { ... },
    None => { ... }
}

我希望这有帮助!