Rust玩具解析器中ASTNode的ToTopLevel如何避免克隆获取所有权?
Rust AST节点转换:避免克隆直接获取ToTopLevel节点所有权
我正在写一个Rust玩具解析器,需要对AST节点执行三种转换操作:
- Remove(移除)
- Keep(保留)
- ToTopLevel(移至顶层)
前两种已经正常实现,但处理ToTopLevel动作时,目前只能克隆节点存入临时Vec。既然这个节点本来就要被删除,我想知道能不能直接获取它的所有权存入临时Vec,而不用克隆。
现有代码
use std::borrow::Cow; #[derive(PartialEq, Clone)] struct ASTNode<'a> { value: Cow<'a, str>, nodes: Vec<ASTNode<'a>> } struct Visitor<'a> { new_node: Vec<ASTNode<'a>> } enum Action { Remove, Keep, ToTopLevel, } impl<'a> Visitor<'a> { fn some_compute_to_determinate_action(&mut self, root: &mut ASTNode<'a>) -> Action { return Action::ToTopLevel; } fn visit(&mut self, root :&mut ASTNode<'a>) -> Action { let mut children_actions: Vec<Action> = Vec::new(); for node in &mut root.nodes { children_actions.push(self.visit(node)); } let mut index: usize = 0; root.nodes.retain(|node| { index+=1; match children_actions[index-1] { Action::Remove => false, Action::Keep => true, Action::ToTopLevel => { // since it would delete node, is there are any way // i can take ownership and push it to new_node vec ? // like : self.new_node.push(*node); self.new_node.push(node.clone()); return false; } } }); return self.some_compute_to_determinate_action(root); } fn push_node_to_top_level_after_visit(&mut self,root: &mut ASTNode<'a>) { root.nodes.append(&mut self.new_node); } } fn main() { let mut root = ASTNode { value: Cow::Borrowed("root_value"), nodes: Vec::new()/* assume there are a a lot of nested children */ }; let mut visitor = Visitor { new_node: Vec::new() }; visitor.visit(&mut root); visitor.push_node_to_top_level_after_visit(&mut root); }
解决方案:替换retain为手动过滤+提取所有权
Vec::retain的闭包只能拿到节点的引用,没法直接获取所有权。要实现直接转移所有权,需要手动遍历节点,把要保留的和要移到顶层的分开处理:
修改后的visit方法:
fn visit(&mut self, root: &mut ASTNode<'a>) -> Action { let mut children_actions: Vec<Action> = Vec::new(); for node in &mut root.nodes { children_actions.push(self.visit(node)); } let mut kept_nodes = Vec::new(); let mut top_level_nodes = Vec::new(); // 清空原节点列表并获取所有节点的所有权,逐个匹配动作处理 for (node, action) in root.nodes.drain(..).zip(children_actions) { match action { Action::Remove => continue, Action::Keep => kept_nodes.push(node), Action::ToTopLevel => top_level_nodes.push(node), } } // 将保留的节点放回原列表 root.nodes = kept_nodes; // 将需要移到顶层的节点合并到临时列表 self.new_node.append(&mut top_level_nodes); self.some_compute_to_determinate_action(root) }
关键改动说明
- 用
root.nodes.drain(..)清空原Vec并获取所有节点的所有权,让我们能直接操作节点的归属 - 通过
zip将节点与对应的动作配对,按需分配节点到保留列表或顶层列表 - 最后把保留列表赋值回原节点的
nodes字段,顶层节点直接转移到visitor.new_node中,完全避免了克隆操作
内容的提问来源于stack exchange,提问作者steven-lie
相关产品推荐
相关产品推荐

