构建向量树

时间:2017-01-30 09:45:07

标签: rust borrow-checker

我试图制作一个简单的LISP解析器,但我仍然坚持将令牌向量转换为AST节点树的步骤。

我创建了树的根,然后将一堆引用保存到我当前想要添加下一个节点的树中。问题在于,无论我尝试什么,似乎借用检查员都认为我引用的东西不够长。

这是代码:

pub fn parse(tokens: &Vec<Token>) -> Node {
    let mut root: Vec<Node> = vec![];

    {
        tokens.into_iter().fold(vec![&mut root], handle_token);
    }

    Node::List(root)
}

fn handle_token<'a>(mut stack: Vec<&'a mut Vec<Node>>, token: &Token) -> Vec<&'a mut Vec<Node>> {
    if *token == Token::LParen {
        let new_node = Node::List(vec![]); // Create the new node
        stack[0].push(new_node); // Add it to the tree
        match stack[0][0] {
            Node::List(ref mut the_vec) => stack.push(the_vec), // Finally, add a mutable reference to the new vector so that subsequent nodes will become children of this Node
            _ => panic!(),
        };
    } else if *token == Token::RParen {
        stack.pop();
    } else {
        match *token {
            Token::Identifier(ref identifier) => {
                stack[0].push(Node::Identifier(identifier.to_owned()))
            }
            Token::Number(number) => stack[0].push(Node::Number(number)),
            Token::Str(ref s) => stack[0].push(Node::Str(s.to_owned())),
            Token::EOF => {}
            _ => panic!(),
        }
    }

    stack
}

这是编译器输出:

error: `stack` does not live long enough
  --> src/parser.rs:30:15
   |
30 |         match stack[0][0] {
   |               ^^^^^ does not live long enough
...
47 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the block at 26:96...
  --> src/parser.rs:26:97
   |
26 | fn handle_token<'a>(mut stack: Vec<&'a mut Vec<Node>>, token: &Token) -> Vec<&'a mut Vec<Node>> {
   |                                                                                                 ^

在对此进行了一些研究之后,似乎我试图做一些完全非惯用于Rust的东西,但我不确定。是否有一种简单的方法可以使这项工作,或者我是否需要重新考虑这一点?

我尝试减少问题to a minimal example

enum Token {
    Start,
    End,
    Value(i32),
}

enum Node {
    List(Vec<Node>),
    Value(i32),
}

fn main() {
    let v = vec![Token::Start, Token::Value(1), Token::End];
    parse(&v);
}

fn parse(tokens: &Vec<Token>) -> Node {
    let mut root: Vec<Node> = vec![];

    {
        tokens.into_iter().fold(vec![&mut root], handle_token);
    }

    Node::List(root)
}

fn handle_token<'a>(mut stack: Vec<&'a mut Vec<Node>>, token: &Token) -> Vec<&'a mut Vec<Node>> {
    match *token {
        Token::Start => {
            stack[0].push(Node::List(vec![])); // Add the new node to the tree
            match stack[0][0] {
                Node::List(ref mut the_vec) => stack.push(the_vec), // Add a mutable reference to the new vector so that subsequent nodes will become children of this Node
                _ => panic!(),
            };
        },
        Token::End => { stack.pop(); },
        Token::Value(v) => stack[0].push(Node::Value(v)),
    }

    stack
}

2 个答案:

答案 0 :(得分:2)

正如@wimh所说,你违反了所有权。让我试着把它分解一下,看看它是否有意义。

stack[0][0]会在Node的可变借用中包含Vec。然后,您尝试可变地借用Vec的{​​{1}}变体中包含的Node::List,并将其作为可变借位添加到外部NodeVec })。如果允许这样做,您现在可以让外部stack和内部Vec能够同时改变Vec Node

我会尝试重新考虑你的设计,看看你是否可以让所有权更清晰。

答案 1 :(得分:1)

在阅读blog post about modeling graphs using vector indices后,我决定尝试类似的方法。生成的代码可以工作并且更加简单:

type NodeIndex = usize;

pub fn parse(tokens: &Vec<Token>) -> Node {
    let mut root: Node = Node::List(vec![]);

    {
        tokens.into_iter().fold((&mut root, vec![]), handle_token);
    }

    root
}

fn add_node(tree: &mut Node, indices: &Vec<NodeIndex>, node: Node) -> NodeIndex {
    let node_to_add_to = get_node(tree, indices);

    match node_to_add_to {
        &mut Node::List(ref mut vec) => {
            vec.push(node);
            vec.len() - 1
        },
        _ => panic!(),
    }
}

fn get_node<'a>(tree: &'a mut Node, indices: &Vec<NodeIndex>) -> &'a mut Node {
    indices.iter().fold(tree, |node, index| match node {
        &mut Node::List(ref mut vec) => &mut vec[*index],
        _ => panic!(),
    })
}

fn handle_token<'a>(state: (&'a mut Node, Vec<NodeIndex>), token: &Token) -> (&'a mut Node, Vec<NodeIndex>) {
    let (tree, mut index_stack) = state;

    match *token {
        Token::LParen => {
            let new_index = add_node(tree, &index_stack, Node::List(vec![]));
            index_stack.push(new_index);
        },
        Token::RParen => { index_stack.pop(); },
        Token::Identifier(ref identifier) => { add_node(tree, &index_stack, Node::Identifier(identifier.to_owned())); },
        Token::Number(number) => { add_node(tree, &index_stack, Node::Number(number)); },
        Token::Str(ref s) => { add_node(tree, &index_stack, Node::Str(s.to_owned())); },
        Token::EOF => { assert!(index_stack.is_empty()); },
    }

    (tree, index_stack)
}