Rust:实现了AVL树,并且错误:线程“ main”对“已经借用:BorrowMutError”感到恐慌

时间:2019-11-29 23:33:13

标签: rust avl-tree

我具有以下树结构:

use std::cell::RefCell;
use std::rc::Rc;
use std::cmp;
use std::cmp::Ordering;

type AVLTree<T> = Option<Rc<RefCell<TreeNode<T>>>>;

#[derive(Debug, PartialEq, Clone)]
struct TreeSet<T: Ord> {
    root: AVLTree<T>,
}

impl<T: Ord> TreeSet<T> {
    fn new() -> Self {
        Self { 
            root: None 
        }
    }

    fn insert(&mut self, value: T) -> bool {
        let current_tree = &mut self.root;

        while let Some(current_node) = current_tree {

            let node_key = &current_node.borrow().key;

            match node_key.cmp(&value) {
                Ordering::Less => { let current_tree = &mut current_node.borrow_mut().right; },
                Ordering::Equal => {
                    return false;
                }
                Ordering::Greater => { let current_tree = &mut current_node.borrow_mut().left; },
            }
        }

        *current_tree = Some(Rc::new(RefCell::new(TreeNode {
            key: value,
            left: None,
            right: None,
            parent: None
        })));

        true
    }

}


#[derive(Clone, Debug, PartialEq)]
struct TreeNode<T: Ord> {
    pub key: T,
    pub parent: AVLTree<T>,
    left: AVLTree<T>,
    right: AVLTree<T>,
}


fn main() {
    let mut new_avl_tree: TreeSet<u32> = TreeSet::new();
    new_avl_tree.insert(3);
    new_avl_tree.insert(5);
    println!("Tree: {:#?}", &new_avl_tree);
}

使用cargo build构建是可以的,但是当我运行cargo run时,出现以下错误:

  

线程“主”对“已借用:BorrowMutError”感到恐慌,src \ libcore \ result.rs:1165:5

     

注意:使用RUST_BACKTRACE=1环境变量运行以显示回溯。错误:流程没有

     

成功退出:target\debug\avl-tree.exe(退出代码:101)

如果我只打电话给insert(3),就可以了,我的树可以正确打印了。但是,如果我在insert(5)之后insert(3),则会收到该错误。

我该如何解决?

2 个答案:

答案 0 :(得分:1)

由于语言中的内存安全规则,对于新手来说,手动实现诸如链表,树,图之类的数据结构不是任务。我建议您阅读Too Many Linked Lists教程,该教程讨论了如何以正确的方式实现Rust的安全列表和不安全链接列表。

也请阅读有关name shadowing的信息。 您的错误是,在一个循环中,您尝试借用可变的东西,而这些东西已经借来的是不可变的。

let node_key = &current_node.borrow().key; // Borrow as immutable

match node_key.cmp(&value) {
    Ordering::Less => { let current_tree = &mut current_node.borrow_mut().right; }, // Create a binding which will be immediately deleted and borrow as mutable.

我建议您阅读Rust book以学习防锈。

答案 1 :(得分:0)

首先让我们更正您的算法。以下几行是错误的:

let current_tree = &mut current_node.borrow_mut().right;
...
let current_tree = &mut current_node.borrow_mut().left;

两者都不会将值重新分配给current_tree,而是创建一个新的(未使用的)值(@Inline将其称为“名称阴影”)。删除let并制作current_tree mut

现在,我们收到编译器错误temporary value dropped while borrowed。可能是编译器错误消息确实误导了您。它告诉您使用let来延长生命周期,如果您在相同的范围内使用结果,这是正确的,但是不允许让生命周期超出范围。

问题是您无法传递对循环所拥有的值的引用(如current_node.borrow_mut.right)。因此,最好使用current_tree作为拥有的变量。可悲的是,这意味着代码中的许多巧妙技巧将不再起作用。

代码中的另一个问题是多重借用问题(您最初的运行时警告与此有关)。您不能在没有惊慌的情况下在同一borrow()上呼叫borrow_mut()RefCell(这是RefCell的目的)。

因此,在找到代码中的问题之后,我对如何编写代码产生了兴趣。现在,它已经写好了,我认为共享它是公平的:     fn insert(&mut self,value:T)-> bool {       如果让None = self.root {         self.root = TreeSet :: root(value);         返回true;       }       让mut current_tree = self.root.clone();

  while let Some(current_node) = current_tree {
    let mut borrowed_node = current_node.borrow_mut();
    match borrowed_node.key.cmp(&value) {
      Ordering::Less => {
        if let Some(next_node) = &borrowed_node.right {
          current_tree = Some(next_node.clone());
        } else {
          borrowed_node.right = current_node.child(value);
          return true;
        }
      }
      Ordering::Equal => {
        return false;
      }
      Ordering::Greater => {
        if let Some(next_node) = &borrowed_node.left {
          current_tree = Some(next_node.clone());
        } else {
          borrowed_node.left = current_node.child(value);
          return true;
        }
      }
    };
  }
  true
}

//...

trait NewChild<T: Ord> {
  fn child(&self, value: T) -> AVLTree<T>;
}
impl<T: Ord> NewChild<T> for Rc<RefCell<TreeNode<T>>> {
  fn child(&self, value: T) -> AVLTree<T> {
    Some(Rc::new(RefCell::new(TreeNode {
      key: value,
      left: None,
      right: None,
      parent: Some(self.clone()),
    })))
  }
}

一个人必须编写两种方法child(value:T)root(value:T)进行编译。