提问人:Pouneh Bahrami 提问时间:4/25/2023 更新时间:4/26/2023 访问量:180
一次不能多次借用可变 + 变量是可变队列
cannot borrow as mutable more than once at a time + the variable is a mutable queue
问:
我正在尝试遍历一个图形,并根据我的代码中定义的条件从中提取一个子树。图表或标准的细节并不重要。然而,由于引用的复杂性和锈蚀中的借用,创建树的过程非常具有挑战性。我不是生锈专家。
为了简单起见,我有 2 个功能:
- 第一个获取图,要遍历的图的深度,并返回一个 ScriptTree 类型的子树,该子树是一个结构。
fn run_subsequent_scripts(&self, graph: &PageGraph, depth: usize) -> ScriptTree {
let mut level: usize;
let mut visited: HashMap<NodeId, bool> = HashMap::new();
// create a queue for BFS
let mut queue = vec![];
// Starting vertex maked as visited and added to queueu
let script_info = get_script_info(graph, self.query.id);
let mut root = ScriptTree::new(script_info.0 ,script_info.1, script_info.2);
visited.insert(self.query.id, true);
queue.push(&mut root);
let mut level_meter: Vec<NodeId> = Vec::new();
// continue until queue is empty
while queue.len() != 0 && level_meter.len() < depth {
level = queue.len();
while level != 0 {
// Get the front of the queue and remove it
let mut child_node: ScriptTree;
let parent_node = queue.remove(0);
level -= 1;
// Get all adjacent vertices from that vertex
// neighbors of the parent
let neighbors: Vec<NodeId> = get_injected_scripts(&graph, parent_node.script_info.script_node_id, &Action::script());
let mut neighbor_nodes: Vec<ScriptTree> = Vec::new();
for child_id in neighbors {
if !visited.contains_key(&child_id) {
//mark as visited
visited.insert(child_id, true);
// push back to check this vertex's vertices
let child_script_info = get_script_info(graph, child_id);
child_node = ScriptTree::new(child_script_info.0, child_script_info.1, child_script_info.2);
queue.push(&mut child_node);
neighbor_nodes.push(child_node);
level_meter.push(child_id);
}
}
self.update_tree(&mut root, &parent_node, neighbor_nodes);
}
level += 1;
}
return root
}
- 第二个函数通过添加在上一个函数中向量的node_neighbors中捕获的parent_node子项来更新树:
fn update_tree(&self, subtree: &mut ScriptTree, parent_node: &ScriptTree, node_neighbors: Vec<ScriptTree>) {
if subtree.node_id == parent_node.node_id {
// Return a reference to this node if it has the target ID
for child in node_neighbors{
subtree.add_child(child);
}
}
else {
// Recursively search the children of this node
for mut child in &subtree.children {
self.update_tree(&mut child, parent_node, node_neighbors);
}
}
}
这段代码在多行中给了我错误:
在函数 1 中,
- 在行中,它说“不能分配给,因为它是借来的,分配到借来的发生在这里”。
child_node = ScriptTree::new(child_script_info.0, child_script_info.1, child_script_info.2);
child_node
child_node
- 在行中,它说“不能借用,因为一次可变多次,在循环的上一次迭代中,这里可变借用”
queue.push(&mut child_node);
child_node
child_node
- 在行中,它说“不能搬出,因为它是借来的,搬出这里发生”
neighbor_nodes.push(child_node);
child_node
child_node
- 并且它说“不能一次多次借用为可变借用,第二次可变借用在这里发生”
self.update_tree(&mut root, &parent_node, neighbor_nodes);
root
在函数 2 中:
- 在行中,它说:
self.update_tree( &mut child, parent_node, node_neighbors);
- 不能将引用中的数据借用为可变 不能借用为可变
&
- 移动值的使用:在循环的上一次迭代中,将值移动到此处
node_neighbors
我感谢任何帮助解决问题!
答:
0赞
Jishan Shaikh
4/26/2023
#1
在:run_subsequent_scripts
- 更改为 .
let mut child_node: ScriptTree
let child_node: ScriptTree
- 声明而不初始化它,这允许稍后分配给它而无需可变借用它。
child_node
- 声明而不初始化它,这允许稍后分配给它而无需可变借用它。
- 更改为 .
queue.push(&mut root)
queue.push(root)
- 将对 root 的不可变引用传递给队列,这应该允许我们稍后可变地借用它。
- 更改为 .
let mut neighbor_nodes: Vec<ScriptTree> = Vec::new()
let mut neighbor_nodes = Vec::with_capacity(neighbors.len())
- 预先分配具有足够容量的neighbor_nodes向量以容纳所有邻居,这应该避免任何可能导致借贷问题的重新分配。
- 更改循环访问要改用的向量。
neighbor_nodes
for child_node in node_neighbors.into_iter()
- 使用向量,并允许在不借用它们的情况下将值移出其中。
node_neighbors
ScriptTree
- 使用向量,并允许在不借用它们的情况下将值移出其中。
在:update_tree
- 更改为 .
for mut child in &subtree.children
for child in &mut subtree.children
- 允许可变地借用子树的子级,这是更新子树所必需的。
更新了包含示例输入的代码 (Rust Playground):
use std::collections::HashMap;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct NodeId(u32);
#[derive(Debug)]
struct PageGraph;
#[derive(Debug, Clone)]
struct ScriptInfo(u32, u32, u32);
#[derive(Debug, Clone)]
struct ScriptTree {
script_info: ScriptInfo,
children: Vec<ScriptTree>,
}
impl ScriptTree {
fn new(a: u32, b: u32, c: u32) -> Self {
ScriptTree {
script_info: ScriptInfo(a, b, c),
children: Vec::new(),
}
}
fn add_child(&mut self, child: ScriptTree) {
self.children.push(child);
}
}
enum Action {
Script,
}
fn get_injected_scripts(
_graph: &PageGraph,
_node_id: u32,
_action: &Action,
) -> Vec<NodeId> {
vec![NodeId(1), NodeId(2), NodeId(3)]
}
fn get_script_info(_graph: &PageGraph, _node_id: NodeId) -> ScriptInfo {
ScriptInfo(1, 2, 3)
}
struct Query {
id: NodeId,
}
struct Example {
query: Query,
}
impl Example {
fn run_subsequent_scripts(&self, graph: &PageGraph, depth: usize) -> ScriptTree {
let mut level: usize = 0;
let mut visited: HashMap<NodeId, bool> = HashMap::new();
let mut queue = vec![];
let script_info = get_script_info(graph, self.query.id.clone());
let mut root = ScriptTree::new(script_info.0, script_info.1, script_info.2);
visited.insert(self.query.id.clone(), true);
queue.push(root.clone());
let mut level_meter: Vec<NodeId> = Vec::new();
while !queue.is_empty() && level < depth {
let level_size = queue.len();
for _ in 0..level_size {
let mut neighbor_nodes = Vec::new();
let parent_node = queue.remove(0);
let neighbors = get_injected_scripts(&graph, parent_node.script_info.0, &Action::Script);
for child_id in neighbors {
if !visited.contains_key(&child_id) {
visited.insert(child_id.clone(), true);
let child_script_info = get_script_info(graph, child_id.clone());
let child_node = ScriptTree::new(
child_script_info.0,
child_script_info.1,
child_script_info.2,
);
queue.push(child_node.clone());
neighbor_nodes.push(child_node);
level_meter.push(child_id);
}
}
self.update_tree(&mut root, &parent_node, neighbor_nodes);
}
level += 1;
}
root
}
fn update_tree(
&self,
subtree: &mut ScriptTree,
parent_node: &ScriptTree,
node_neighbors: Vec<ScriptTree>,
) {
if subtree.script_info.0 == parent_node.script_info.0 {
for child_node in node_neighbors {
subtree.add_child(child_node);
}
} else {
for child in &mut subtree.children {
self.update_tree(child, parent_node, node_neighbors.clone());
}
}
}
}
fn main() {
let graph = PageGraph;
let example = Example {
query: Query { id: NodeId(1) },
};
let depth = 1;
let script_tree = example.run_subsequent_scripts(&graph, depth);
println!("{:#?}", script_tree);
}
最佳实践:提供可由其他人执行的最小可行示例代码(有问题)。
评论
0赞
Pouneh Bahrami
4/26/2023
嗨,@jishan-shaikh,感谢您的详细回答!我做了你建议的更改。它提示其他错误:现在在 update_tree 函数中,在说“for child in neighbor_nodes { subtree.add_child(child); }”的行中,child 是不匹配类型,并说 expected struct ,找到。当我尝试更改为 struct ScriptTree 实现的 add_child 方法时,它会导致其他错误。比如,我必须将子项更改为 Vec<&Scripts>这要求我包含“预期的命名生存期参数”ScriptTree
&&ScriptTree
0赞
Jishan Shaikh
4/26/2023
@PounehBahrami 编辑:添加了 playground 以供参考和示例
0赞
Pouneh Bahrami
4/28/2023
谢谢@jishan-shaikh!!真的很感激它♡解决了这个问题。我真的为此苦苦挣扎了一个星期:)),似乎我不能投票给你的答案,因为我在 stackoverflow 上没有足够的声誉!对不起......
0赞
Jishan Shaikh
4/28/2023
谢谢,很乐意帮忙。不用担心点赞,我想你仍然可以接受答案。
评论
&subtree.children
mut child
node_neighbours
&mut ScriptTree
child_node