rust 如何在需要可变引用的函数上循环?

new9mtju  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(200)

我是相当新的生 rust ,我有一个问题,借检查。
我有这样一个函数:

impl<'a> DFA<'a> {
    fn solve_next(&mut self, c: Option<char>) -> Option<TokenType> {
        let node = self.state.unwrap_or_else(|| &self.start_node);
        let checks: Vec<char> = node.connections.iter().map(|x| x.1).collect();
        let mut position = None;
        if let Some(c) = c {
            position = checks.iter().position(|x| *x == c);
        }
        if let Some(i) = position {
            let node = &node.connections[i].0;
            self.state = Some(node);
            None
        } else {
            if node.is_end {
                return Some(TokenType::Keyword);
            }
            panic!("Invalid charter for current set");
        }
    }
}

此函数在循环完成时返回TokenType(枚举),在循环未完成时返回None。它位于此结构上:

struct DFA<'a> {
    state: Option<&'a Node>,
    start_node: Node,
}

节点为:

struct Node {
    connections: Vec<(Node, char)>,
    is_end: bool,
    token_type: TokenType,
}

本发明的方法

fn insert(&mut self, _match: char, end: bool) -> &mut Node {

它创建并返回一个新节点,并将该节点添加到它自己连接中。
因此,当我想循环使用solve next函数时,我尝试:

impl<'a> Lexer<'a> {
    fn next_char(&self) -> Option<char> {
        let mut r: Option<char> = None;
        for c in &self.chars {
            match c {
                ' ' | '\n' | '\r' => {
                    continue;
                }
                _ => {
                    r = Some(c.clone());
                    break;
                }
            }
        }
        return r;
    }
    fn next_token(&'a mut self) {
        let somechar = 'c';
        let mut x = self.dfa.solve_next(self.next_char());
        while let None = x {
            x = self.dfa.solve_next(self.next_char());
        }
    }
}

该方法

struct Lexer<'a> {
    //the output of the lexer
    pub tokens: Vec<Token>,

    chars: Vec<char>,

    dfa: DFA<'a>,
}

编译错误是

error[E0499]: cannot borrow `self.dfa` as mutable more than once at a time
   --> src/main.rs:177:17
    |
146 | impl<'a> Lexer<'a> {
    |      -- lifetime `'a` defined here
...
175 |         let mut x = self.dfa.solve_next(self.next_char());
    |                     -------------------------------------
    |                     |
    |                     first mutable borrow occurs here
    |                     argument requires that `self.dfa` is borrowed for `'a`
176 |         while let None = x {
177 |             x = self.dfa.solve_next(self.next_char());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here

error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
   --> src/main.rs:177:37
    |
146 | impl<'a> Lexer<'a> {
    |      -- lifetime `'a` defined here
...
175 |         let mut x = self.dfa.solve_next(self.next_char());
    |                     -------------------------------------
    |                     |
    |                     mutable borrow occurs here
    |                     argument requires that `self.dfa` is borrowed for `'a`
176 |         while let None = x {
177 |             x = self.dfa.solve_next(self.next_char());
    |                                     ^^^^^^^^^^^^^^^^ immutable borrow occurs here

那么,我可以在这个循环中使用solve_next吗?因为我不知道如何创建一个函数,它可以像这样使用,而不需要一个可变的引用。

inkz8wg9

inkz8wg91#

您的程式码实际发生的错误如下(playground):

error: lifetime may not live long enough
  --> src/lib.rs:27:49
   |
25 | impl<'a> DFA<'a> {
   |      -- lifetime `'a` defined here
26 |     fn solve_next(&mut self, c: Option<char>) -> Option<TokenType> {
   |                   - let's call the lifetime of this reference `'1`
27 |         let node = self.state.unwrap_or_else(|| &self.start_node);
   |                                                 ^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

这实际上是在抱怨对self.start_node的引用活得不够长,也就是说至少对'a来说。
原因是你的局部变量node,类型为&'x Node,生存期为'x,是self.state.unwrap()&self.start_node的生存期中较短的一个。第一个是'a,第二个是&self的未命名生存期(从现在开始是's)。
但是根据Rust寿命规则,'aDFA<'a>长,Self's长,并且Self等于DFA<'a>,那么我们得出'a's长,所以局部node的寿命实际上是's .
另一个关键词是:

self.state = Some(node);

因为self.state的类型是Option<&'a Node>,这要求node的生存期,也就是's,比'a长,但这是不可能的,我们已经确定它是相反的:'a's存在更长时间。因此出现编译器错误。
简而言之,你试图写一个自引用结构体,在一个字段中存储对另一个字段的引用,而这是众所周知的不可能的。
在您的问题的原始版本中,您试图通过向solve_next()函数添加额外的生存期来解决该问题:

fn solve_next(&'a mut self, c: Option<char>) -> Option<TokenType>

这样就强制's'a完全相等,所以这个函数的主体是正确的。不幸的是,如果你试图用以下语句调用它:

fn next_token(&mut self) {
        let mut x = self.dfa.solve_next(self.next_char());
    }

失败原因:

error: lifetime may not live long enough
  --> src/lib.rs:63:21
   |
46 | impl<'a> Lexer<'a> {
   |      -- lifetime `'a` defined here
...
62 |     fn next_token(&mut self) {
   |                   - let's call the lifetime of this reference `'1`
63 |         let mut x = self.dfa.solve_next(self.next_char());
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`

原因与上述完全相同:self的匿名生存期必须比'a长,但是编译器以相反的方式推导它。
当然,您也可以用同样的方法 * 修复 * 它:

fn next_token(&'a mut self) {

只要不尝试两次调用solve_next(),它就会编译。
我不知道为什么调用solve_next()编译一次,但调用它两次都失败了。但实际上这并不重要,因为即使这个函数工作,使用这个额外的&'a mut self,从外部代码调用这个函数仍然会有同样的问题。
那么解决方案呢?我认为你需要重构代码,这样你就永远不会在同一个结构体中添加一个引用start_node
例如,您可以将start_node储存在这个结构之外:

struct DFA<'a> {
    state: Option<&'a Node>,
    start_node: &'a Node,
}

这样,并从&'a self中删除所有的生存期注解,它将只编译(playground)。

相关问题