在实现二叉搜索树时,不能多次将节点借用为可变节点

时间:2016-06-26 21:30:12

标签: rust binary-search-tree

我正在尝试在Rust中实现二进制搜索树,我遇到插入元素的问题。在Rust中这样做的惯用方法是什么?

这是我的实施:

use std::cmp::Ordering;

pub struct BinarySearchTree {
    root: OptNode,
    size: u32,
}

type OptNode = Option<Box<Node>>;

struct Node {
    key: i32,
    left: OptNode,
    right: OptNode,
}

impl BinarySearchTree {
    pub fn new() -> Self {
        BinarySearchTree {
            root: None,
            size: 0,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.size == 0
    }

    pub fn size(&self) -> u32 {
        self.size
    }

    pub fn contains(&self, key: i32) -> bool {
        let mut node = &self.root;
        while let Some(ref boxed_node) = *node {
            match key.cmp(&boxed_node.key) {
                Ordering::Less => node = &boxed_node.left,
                Ordering::Greater => node = &boxed_node.right,
                Ordering::Equal => return true,
            }
        }

        false
    }

    pub fn insert(&mut self, key: i32) {
        let mut node = &mut self.root;
        while let Some(ref mut boxed_node) = *node {
            match key.cmp(&boxed_node.key) {
                Ordering::Less => node = &mut boxed_node.left,
                Ordering::Greater => node = &mut boxed_node.right,
                Ordering::Equal => return,
            }
        }

        *node = Some(Box::new(Node {
            key: key,
            left: None,
            right: None,
        }));
    }
}

fn main() {}

以下是我遇到的错误:

error[E0499]: cannot borrow `node.0` as mutable more than once at a time
  --> src/main.rs:47:24
   |
47 |         while let Some(ref mut boxed_node) = *node {
   |                        ^^^^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop
...
60 |     }
   |     - mutable borrow ends here

error[E0506]: cannot assign to `node` because it is borrowed
  --> src/main.rs:49:35
   |
47 |         while let Some(ref mut boxed_node) = *node {
   |                        ------------------ borrow of `node` occurs here
48 |             match key.cmp(&boxed_node.key) {
49 |                 Ordering::Less => node = &mut boxed_node.left,
   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `node` occurs here

error[E0506]: cannot assign to `node` because it is borrowed
  --> src/main.rs:50:38
   |
47 |         while let Some(ref mut boxed_node) = *node {
   |                        ------------------ borrow of `node` occurs here
...
50 |                 Ordering::Greater => node = &mut boxed_node.right,
   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `node` occurs here

error[E0506]: cannot assign to `*node` because it is borrowed
  --> src/main.rs:55:9
   |
47 |           while let Some(ref mut boxed_node) = *node {
   |                          ------------------ borrow of `*node` occurs here
...
55 | /         *node = Some(Box::new(Node {
56 | |             key: key,
57 | |             left: None,
58 | |             right: None,
59 | |         }));
   | |___________^ assignment to borrowed `*node` occurs here

3 个答案:

答案 0 :(得分:4)

Rust的编译器还不够复杂(但是?)来处理这种情况。 Rust看到你试图不止一次地多次尝试借用相同的值,因为它在循环中看到对同一个变量的重复可变借位。当然,这并不是你想要做的事情,因为你想在每次迭代时重新分配变量,但Rust并不支持分配给被借用的变量。

我们需要做的是拥有中间变量,以便编译器可以正确地跟踪借用。我们如何创建不确定数量的变量?随着递归!

impl BinarySearchTree {
    pub fn insert(&mut self, key: i32) {
        fn insert_node(node: &mut OptNode, key: i32) {
            if let Some(ref mut boxed_node) = *node {
                match key.cmp(&boxed_node.key) {
                    Ordering::Less => insert_node(&mut boxed_node.left, key),
                    Ordering::Greater => insert_node(&mut boxed_node.right, key),
                    Ordering::Equal => return,
                }
            } else {
                *node = Some(Box::new(Node { key: key, left: None, right: None}));
            }
        }

        insert_node(&mut self.root, key)
    }
}

注意:虽然这个算法是尾递归的,但是Rust并没有将它优化为尾调用,所以它可能会在退化情况下导致堆栈溢出。

答案 1 :(得分:3)

没有递归:

pub fn insert(&mut self, key: i32) {
    let mut node = &mut self.root;
    loop {
        node = match node.as_ref().map(|n| key.cmp(&n.key)) {
            Some(Ordering::Less) => &mut { node }.as_mut().unwrap().left,
            Some(Ordering::Equal) => return,
            Some(Ordering::Greater) => &mut { node }.as_mut().unwrap().right,
            None => {
                *node = Some(Box::new(Node {
                    key: key,
                    left: None,
                    right: None,
                }));
                return;
            }
        };
    }
}

unwrap()在这里很安全。

  

请您详细说明{node}.as_mut()...工作的原因

node是一个可变引用(&mut Option<Box<Node>>)。它无法复制。

let temp = node;

此处node移动temp。这正是我们需要避免分配给借来的node。我们可以移动 node到新的临时变量并借用它。

// ...
Some(Ordering::Less) => {
    let temp = node;
    &mut temp.as_mut().unwrap().left
}
// ...

紧凑符号:

// ...
Some(Ordering::Less) =>  &mut { let temp = node; temp }.as_mut().unwrap().left,
// ...

表达式{ node }{ let temp = node; temp }是等效的,但在第一种情况下,node 移动到隐式临时变量。

答案 2 :(得分:1)

作为Francis Gagné said

  

Rust的编译器不够复杂(但是?)

即将来临,它被称为non-lexical lifetimes。启用它们后,您的原始代码将按原样运行:

#![feature(nll)]

use std::cmp::Ordering;

pub struct BinarySearchTree {
    root: OptNode,
    size: u32,
}

type OptNode = Option<Box<Node>>;

struct Node {
    key: i32,
    left: OptNode,
    right: OptNode,
}

impl BinarySearchTree {
    pub fn new() -> Self {
        BinarySearchTree {
            root: None,
            size: 0,
        }
    }

    pub fn is_empty(&self) -> bool {
        self.size == 0
    }

    pub fn size(&self) -> u32 {
        self.size
    }

    pub fn contains(&self, key: i32) -> bool {
        let mut node = &self.root;
        while let Some(ref boxed_node) = *node {
            match key.cmp(&boxed_node.key) {
                Ordering::Less => node = &boxed_node.left,
                Ordering::Greater => node = &boxed_node.right,
                Ordering::Equal => return true,
            }
        }

        false
    }

    pub fn insert(&mut self, key: i32) {
        let mut node = &mut self.root;
        while let Some(ref mut boxed_node) = *node {
            match key.cmp(&boxed_node.key) {
                Ordering::Less => node = &mut boxed_node.left,
                Ordering::Greater => node = &mut boxed_node.right,
                Ordering::Equal => return,
            }
        }

        *node = Some(Box::new(Node {
            key: key,
            left: None,
            right: None,
        }));
    }
}

fn main() {}