这段代码可以编译:
#[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
运算符(即必须使用mut
)。通过传递可变引用而不是不可变引用,可以解决“无法将不可变借用内容作为可变”错误。
- 将
node_index == node_count
分支更改为直接修改值,而不是尝试原地覆盖。这通过根本没有进行任何移动来解决“无法移动租借内容”的错误。
实际上,可以通过精心使用std::mem::replace
来进行覆盖,以交换新值(例如,使用Value(0)
,因为它很便宜)到left
和right
引用中。 replace
函数返回之前存在的值,即你需要创建新分支的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
的值是指向node
旧内容的指针,因此对于编译器来说(目前),覆盖node
将使这些指针失效,并导致使用它们的任何进一步代码都被破坏(当然,我们可以看到它们都不再被使用,但编译器还没有注意到这样的事情)。幸运的是,有一个简单的解决方案:两个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)
}
};
(注意,操作顺序有点奇怪,与let new_val = match node {...}; *node = new_val;
相同。)
然而,这比我上面写的方式更昂贵,因为它必须为新的Branch
分配2个新的盒子,而原地修改的那个不必这样做。
稍微“更好”的版本可能是(注释内联):
#[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);
}
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 {
match *node {
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
}