0

I have a parser toy that wants to transform AST value like SCSS nested rules, I got a problem is that when I visit every node, I would transform AST node's value computed by parent value and ancestor node value. So I use a context property of visitor to keep that value. but because I am recursively traversal, I need to change context value when iterating children like DFS, and rust's ownership would focus me clone every time when I need to change value every time, but actually, those values existed in the parent node. Is there any method I can use to reference existing value ?

use std::borrow::Cow;
struct ASTNode<'a> {
    value: Cow<'a, str>,
    nodes: Vec<ASTNode<'a>>
}
struct Visitor {
    context: String,
}
fn some_action_to_change_ast_value_with_context(root: &mut ASTNode, context: &str ) {
    // do something like root.value.push(context)
}
impl Visitor {
    fn visit(&mut self, root :&mut ASTNode) {
        some_action_to_change_ast_value_with_context(root, self.context.as_str());
        for node in &mut root.nodes {
            let last_context = self.context; // there need to be clone, can not move ownership from a reference
            self.context = root.value.to_mut().clone();
            self.visit(node);
            self.context = last_context;
        }
    }
}
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 {
        context: String::new()
    };
    visitor.visit(&mut root);
}
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
steven-lie
  • 75
  • 2
  • 7

1 Answers1

1

This type of problem can be solved with functions in the std::mem::{swap, replace, take} family. In your case, replace() fits:

for node in &mut root.nodes {
    let last_context = std::mem::replace(&mut self.context, root.value.to_mut().clone());
    self.visit(node);
    self.context = last_context;
}

However, it might be possible to do something else. The fact that you overwrite the data stored in the Visitor at every step means that you might as well create a separate Visitor for each recursion level:

impl Visitor {
    fn visit(&self, root :&mut ASTNode) {
        some_action_to_change_ast_value_with_context(root, self.context.as_str());
        let child_visitor = Visitor { context: root.value.to_string() };
        for node in &mut root.nodes {
            child_visitor.visit(node);
        }
    }
}

Note that now the Visitors don't need to be mutable.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108