如何在递归该结构时更改可变结构时避免克隆部分
How to avoid cloning parts when changing a mutable struct while recursion over that struct
我尝试递归地遍历一棵玫瑰树。以下代码也可以按预期工作,但由于借用检查器的问题,我仍然需要克隆该值。因此,如果有一种方法可以从克隆更改为更好的东西,那就太好了。
没有 clone() rust 抱怨(正确地)我通过查看 child 节点和闭包中的第二次来借用 self mutable。
整个结构和代码比下面显示的更复杂和更大,但这是核心元素。我是否必须更改数据结构或者我是否遗漏了一些明显的东西?如果数据结构是问题,您将如何更改它?
此外,NType 枚举在这里似乎有点用处,但我还有一些其他类型需要考虑。这里内部节点总是 children 而外部节点永远不会。
enum NType{
Inner,
Outer
}
#[derive(Eq, PartialEq, Clone, Debug)]
struct Node {
// isn't a i32 actually. In my real program it's another struct
count: i32,
n_type: NType,
children: Option<Vec<usize>>
}
#[derive(Eq, PartialEq, Clone, Debug)]
struct Tree {
nodes: Vec<Node>,
}
impl Tree{
pub fn calc(&mut self, features: &Vec<i32>) -> i32{
// root is the last node
self.calc_h(self.nodes.len() - 1, features);
self.nodes[self.nodes.len() - 1].count.clone()
}
fn calc_h(&mut self, current: usize, features: &Vec<i32>){
// do some other things to decide where to go into recursion and where not to
// also use the features
if self.nodes[current].n_type == Inner{
//cloneing is very expensiv and destroys the performance
self.nodes[current].children.as_ref().unwrap().clone().iter().for_each(|&n| self.calc_h(n, features));
self.do_smt(current)
}
self.do_smt(current)
}
}
编辑:
- Lagerbaer 建议使用 as_mut,但这会导致当前成为一个 &mut usize,并不能真正解决问题。
- 已将 childs 更改为 children
child 的正确复数形式是 children,这就是我将在本回答中提及的内容。大概这就是 childs
在您的代码中的意思。
由于node.children
已经是一个Option
,最好的解决方案是.take()
在迭代开始时从节点中取出向量,并在迭代结束时将其放入.这样我们就避免了在迭代过程中持有对 tree.nodes
的引用。
if self.nodes[current].n_type == Inner {
let children = self.nodes[current].children.take().unwrap();
for &child in children.iter() {
self.calc_h(child, features);
}
self.nodes[current].children = Some(children);
}
请注意,在循环的情况下,行为与您的原始代码不同,但如果正确实现树的其余部分,则无需担心。
我尝试递归地遍历一棵玫瑰树。以下代码也可以按预期工作,但由于借用检查器的问题,我仍然需要克隆该值。因此,如果有一种方法可以从克隆更改为更好的东西,那就太好了。
没有 clone() rust 抱怨(正确地)我通过查看 child 节点和闭包中的第二次来借用 self mutable。
整个结构和代码比下面显示的更复杂和更大,但这是核心元素。我是否必须更改数据结构或者我是否遗漏了一些明显的东西?如果数据结构是问题,您将如何更改它?
此外,NType 枚举在这里似乎有点用处,但我还有一些其他类型需要考虑。这里内部节点总是 children 而外部节点永远不会。
enum NType{
Inner,
Outer
}
#[derive(Eq, PartialEq, Clone, Debug)]
struct Node {
// isn't a i32 actually. In my real program it's another struct
count: i32,
n_type: NType,
children: Option<Vec<usize>>
}
#[derive(Eq, PartialEq, Clone, Debug)]
struct Tree {
nodes: Vec<Node>,
}
impl Tree{
pub fn calc(&mut self, features: &Vec<i32>) -> i32{
// root is the last node
self.calc_h(self.nodes.len() - 1, features);
self.nodes[self.nodes.len() - 1].count.clone()
}
fn calc_h(&mut self, current: usize, features: &Vec<i32>){
// do some other things to decide where to go into recursion and where not to
// also use the features
if self.nodes[current].n_type == Inner{
//cloneing is very expensiv and destroys the performance
self.nodes[current].children.as_ref().unwrap().clone().iter().for_each(|&n| self.calc_h(n, features));
self.do_smt(current)
}
self.do_smt(current)
}
}
编辑:
- Lagerbaer 建议使用 as_mut,但这会导致当前成为一个 &mut usize,并不能真正解决问题。
- 已将 childs 更改为 children
child 的正确复数形式是 children,这就是我将在本回答中提及的内容。大概这就是 childs
在您的代码中的意思。
由于node.children
已经是一个Option
,最好的解决方案是.take()
在迭代开始时从节点中取出向量,并在迭代结束时将其放入.这样我们就避免了在迭代过程中持有对 tree.nodes
的引用。
if self.nodes[current].n_type == Inner {
let children = self.nodes[current].children.take().unwrap();
for &child in children.iter() {
self.calc_h(child, features);
}
self.nodes[current].children = Some(children);
}
请注意,在循环的情况下,行为与您的原始代码不同,但如果正确实现树的其余部分,则无需担心。