在Rust中更改树中的节点

时间:2015-01-18 10:08:42

标签: rust borrow-checker

我尝试编写一个函数,在给定树结构的情况下,返回该树的副本,但在特定索引处更改了节点。以下是我到目前为止的情况:

#[derive(Clone)]
pub enum Node {
    Value(u32),
    Branch(u32, Box<Node>, Box<Node>),
}

fn main() {
    let root = Node::Branch(1, Box::new(Node::Value(2)), Box::new(Node::Value(3)));
    zero_node(&root, 2);
}

pub fn zero_node (tree: &Node, node_index: u8) -> Node {

    let mut new_tree = tree.clone();

    fn zero_rec (node : &mut Node, node_count : u8, node_index : u8) -> u8 {
        if (node_index == node_count) {
            match node {
                &mut Node::Value(_) => { *node = Node::Value(0); },
                &mut Node::Branch(_, ref mut left, ref mut right) => { *node = Node::Branch(0, *left, *right);  }
            }
            node_count
        } else {
            match node {
                &mut Node::Value(val) => {1},
                &mut Node::Branch(_, ref mut left, ref mut right) => {
                    let count_left = zero_rec(&**left, node_count + 1, node_index);
                    let count_right = zero_rec(&**right, node_count + 1 + count_left, node_index);
                    count_left + count_right + 1
                }
            }
        }
    }

    zero_rec(&new_tree, 0, node_index);

    new_tree

}

http://is.gd/YdIm0g

我似乎无法摆脱以下错误:&#34;无法借用不可变借来的内容作为可变的&#34;并且&#34;无法摆脱借来的内容&#34;。

我可以根据原始创建新树,并在流程中更改一个节点。但我想了解如何通过借阅检查员赢得这场斗争。

1 个答案:

答案 0 :(得分:8)

此代码编译:

#[derive(Clone)]
pub enum Node {
    Value(u32),
    Branch(u32, Box<Node>, Box<Node>),
}

fn main() {
    let root = Node::Branch(1, Box::new(Node::Value(2)), Box::new(Node::Value(3)));
    zero_node(&root, 2);
}

pub fn zero_node (tree: &Node, node_index: u8) -> Node {

    let mut new_tree = tree.clone();

    fn zero_rec (node : &mut Node, node_count : u8, node_index : u8) -> u8 {
        if node_index == node_count {
            match node {
                &mut Node::Value(ref mut val) => { *val = 0; },
                &mut Node::Branch(ref mut val, _, _) => { *val = 0; }
            }
            node_count
        } else {
            match node {
                &mut Node::Value(_) => {1},
                &mut Node::Branch(_, ref mut left, ref mut right) => {
                    let count_left = zero_rec(&mut **left, node_count + 1, node_index);
                    let count_right = zero_rec(&mut **right, node_count + 1 + count_left, node_index);
                    count_left + count_right + 1
                }
            }
        }
    }

    zero_rec(&mut new_tree, 0, node_index);

    new_tree

}

我所做的改变是:

  • &new_tree&mut new_tree&**left&mut **left等:创建&mut T引用的方式是使用&mut运算符(即mut是必要的)。这通过传递可变引用而不是不可变的
  • 来修复cannot borrow immutable borrowed content as mutable错误
  • 更改node_index == node_count分支以直接改变值,而不是尝试在适当位置覆盖。这可以解决cannot move out of borrowed content错误,只是根本不做任何动作。

实际上可以通过谨慎使用std::mem::replace来实现覆盖,以便将新值(例如Value(0)交换成left以及rightreplace以及left引用。 right函数返回之前存在的值,即完成创建新分支所需的match&mut Node::Branch(_, ref mut left, ref mut right) => { let l = mem::replace(left, Box::new(Node::Value(0))); let r = mem::replace(right, Box::new(Node::Value(0))); *node = Node::Branch(0, l , r); } 内的内容。对相关use std::mem;部分的此更改看起来有点像:

<anon>:25:9: 25:39 error: cannot assign to `*node` because it is borrowed
<anon>:25                   *node = Node::Branch(0, l , r); 
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:22:26: 22:38 note: borrow of `*node` occurs here
<anon>:22               &mut Node::Branch(_, ref mut left, ref mut right) => { 
                                             ^~~~~~~~~~~~

(已将left添加到文件顶部。)

然而它遇到了一个新错误:

right

nodenode值是深入match旧内容的指针,因此,就编译器所知(目前)而言,覆盖node将使那些会导致使用它们的其他代码被破坏的指针无效(当然,我们可以看到它们都没有被更多地使用,但是编译器还没有注意到这样的事情)。幸运的是,这是一个简单的解决方法:两个match武器都将node设置为新值,因此我们可以使用*node = match node { &mut Node::Value(_) => Node::Value(0), &mut Node::Branch(_, ref mut left, ref mut right) => { let l = mem::replace(left, Box::new(Node::Value(0))); let r = mem::replace(right, Box::new(Node::Value(0))); Node::Branch(0, l , r) } }; 来计算新值,然后设置{{1}在进行计算之后:

let new_val = match node { ... }; *node = new_val;

(注意:操作顺序有点奇怪,与Branch相同。)

然而,这比我上面写的要贵,因为它必须为新的#[derive(Clone, Show)] pub enum Node { Value(u32), Branch(u32, Box<Node>, Box<Node>), } fn main() { let root = Node::Branch(1, Box::new(Node::Value(2)), Box::new(Node::Value(3))); let root = zero_node(root, 2); println!("{:?}", root); } // Taking `tree` by value (i.e. not by reference, &) possibly saves on // `clone`s: the user of `zero_node can transfer ownership (with no // deep cloning) if they no longer need their tree. // // Alternatively, it is more flexible for the caller if it takes // `&mut Node` and returns () as it avoids them having to be careful // to avoid moving out of borrowed data. pub fn zero_node (mut tree: Node, node_index: u8) -> Node { fn zero_rec (node : &mut Node, node_count : u8, node_index : u8) -> u8 { if node_index == node_count { // dereferencing once avoids having to repeat &mut a lot match *node { // it is legal to match on multiple patterns, if they bind the same // names with the same types Node::Value(ref mut val) | Node::Branch(ref mut val, _, _) => { *val = 0; }, } node_count } else { match *node { Node::Value(_) => 1, Node::Branch(_, ref mut left, ref mut right) => { let count_left = zero_rec(&mut **left, node_count + 1, node_index); let count_right = zero_rec(&mut **right, node_count + 1 + count_left, node_index); count_left + count_right + 1 } } } } zero_rec(&mut tree, 0, node_index); tree } 分配2个新的盒子,而那个就地修改的盒子不会#39; t必须这样做。


稍微好一点&#34;版本可能是(评论内联):

{{1}}