【问题标题】:Cannot borrow node as mutable more than once while implementing a binary search tree在实现二叉搜索树时不能多次借用节点作为可变节点
【发布时间】:2018-05-26 01:13:56
【问题描述】:

我正在尝试在 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

【问题讨论】:

    标签: rust binary-search-tree


    【解决方案1】:

    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 并未将其优化为尾调用,因此在退化情况下可能会导致堆栈溢出。

    【讨论】:

    • 谢谢!有没有一种不递归的方法?
    【解决方案2】:

    没有递归:

    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 是一个可变引用 (&amp;mut Option&lt;Box&lt;Node&gt;&gt;)。它不能被复制。

    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 移动 到一个隐式临时变量。

    【讨论】:

    • 您能否详细说明为什么{node}.as_mut()... 有效,但node.as_mut()... 最终会出现借用错误?
    【解决方案3】:

    作为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() {}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多