You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust可变借用冲突及队列操作编译错误求助

Rust图遍历与子树构建的借用错误解决

我在Rust中实现图遍历并提取子树时,遇到了借用检查器的阻碍。编写了两个核心函数:run_subsequent_scripts(通过BFS遍历图生成ScriptTree类型子树)和update_tree(为指定父节点添加子节点),但编译时出现多处错误,具体代码和错误信息如下:

相关代码

函数1:run_subsequent_scripts

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
  }

函数2:update_tree

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);
      }
    }    
  }

编译错误详情

run_subsequent_scripts中的错误

  • 行child_node = ScriptTree::new(...):无法赋值给child_node,因为它已被借用
  • 行queue.push(&mut child_node):无法同时多次可变借用child_node,上一轮循环已进行可变借用
  • 行neighbor_nodes.push(child_node):无法移出child_node,因为它已被借用
  • 行self.update_tree(&mut root, &parent_node, neighbor_nodes):无法同时多次可变借用root,第二次可变借用在此处

update_tree中的错误

  • 行self.update_tree( &mut child, parent_node, node_neighbors):
    1. 无法对&引用的数据进行可变借用
    2. 使用了已移动的值node_neighbors,上一轮循环已移动该值

错误修复方案

1. 修复run_subsequent_scripts的核心问题

问题根源

  • 队列存储局部变量child_node的可变引用,会导致引用悬空;同时同一变量被同时借用和移动,违反Rust借用规则。
  • parent_node是root的可变引用,同时传递&mut root给update_tree,导致同一数据存在多个可变引用。

修复措施

放弃在队列中存储节点引用,改为存储节点ID,通过辅助函数在树中查找对应可变节点;同时在BFS过程中直接为父节点添加子节点,避免遍历整棵树的额外开销和借用冲突。

修改后的代码示例:

fn run_subsequent_scripts(&self, graph: &PageGraph, depth: usize) -> ScriptTree {
    let mut visited: HashMap<NodeId, bool> = HashMap::new();
    // 队列存储(父节点ID, 当前节点深度)
    let mut queue = Vec::new();
    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((self.query.id, 0));

    while let Some((parent_id, current_depth)) = queue.pop() {
        if current_depth >= depth {
            break;
        }
        // 找到父节点的可变引用
        let parent_node = find_mut_node(&mut root, parent_id).expect("Parent node not found");
        let neighbors = get_injected_scripts(&graph, parent_node.script_info.script_node_id, &Action::script());
        
        for child_id in neighbors {
            if !visited.contains_key(&child_id) {
                visited.insert(child_id, true);
                let child_script_info = get_script_info(graph, child_id);
                let child_node = ScriptTree::new(child_script_info.0, child_script_info.1, child_script_info.2);
                parent_node.add_child(child_node);
                queue.push((child_id, current_depth + 1));
            }
        }
    }

    root
}

// 辅助函数:递归查找树中对应ID的可变节点
fn find_mut_node(node: &mut ScriptTree, target_id: NodeId) -> Option<&mut ScriptTree> {
    if node.node_id == target_id {
        return Some(node);
    }
    for child in &mut node.children {
        if let Some(found) = find_mut_node(child, target_id) {
            return Some(found);
        }
    }
    None
}

2. 修复update_tree的问题(若需保留该函数)

问题根源

  • for mut child in &subtree.children得到的是不可变引用的可变绑定,无法获取可变引用。
  • node_neighbors是Vec<ScriptTree>,第一次循环时所有权被移动,后续循环无法复用。

修复措施

  • 遍历子节点时使用&mut subtree.children获取可变引用。
  • 通过split_off或克隆(需ScriptTree实现Clone)传递node_neighbors的所有权,避免移动后无法复用。

修改后的代码示例:

fn update_tree(&self, subtree: &mut ScriptTree, parent_node: &ScriptTree, mut node_neighbors: Vec<ScriptTree>) {
    if subtree.node_id == parent_node.node_id {
        for child in node_neighbors.drain(..) {
            subtree.add_child(child);
        }
    } else {
        // 获取子节点的可变引用
        for child in &mut subtree.children {
            // 拆分出当前要传递的子节点列表,避免所有权一次性移动
            self.update_tree(child, parent_node, node_neighbors.split_off(0));
        }
    }
}

推荐方案:优先采用第一种修复方式,直接在BFS过程中完成子节点添加,既解决借用问题又提升代码效率。

内容的提问来源于stack exchange,提问作者Pouneh Bahrami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:49:54