不能借用 `*self` 作为不可变的,因为它也被借用为可变的

时间:2021-03-17 18:28:02

标签: rust

我应该如何解决这个问题,我最终会先进行可变借用,然后再进行不可变借用。

使用 clone() 或添加不同的作用域仍然无效,仍然会出现编译错误。

我在下面的操场中添加了完整的代码: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f576dd99feec48c86e5ef4e986aefa17

代码:

use std::str::Chars;

pub(crate) struct Cursor<'a> {
    source: &'a str,
    cursor_pos: usize,
    chars: Chars<'a>,
}

pub(crate) const EOF_CHAR: char = '\0';

impl<'a> Cursor<'a> {
    pub(crate) fn new(input: &'a str) -> Cursor<'a> {
        Cursor {
            source: input,
            cursor_pos: 0,
            chars: input.chars(),
        }
    }

    fn nth_char(&self, n: usize) -> char {
        self.chars().nth(n).unwrap_or(EOF_CHAR)
    }

    pub(crate) fn first(&self) -> char {
        self.nth_char(0)
    }

    fn chars(&self) -> Chars<'a> {
        self.chars.clone()
    }
    pub(crate) fn bump(&mut self) -> Option<char> {
        let c = self.chars.next()?;
        self.cursor_pos += 1;
        Some(c)
    }

    pub(crate) fn cursor_pos(&self) -> usize {
        self.cursor_pos
    }

    pub(crate) fn substring(&self, start_pos: usize, end_pos: usize) -> &str {
        &self.source[start_pos .. end_pos]
    }
}


#[derive(Debug)]
pub enum TokenType<'a> {
  Equal,
  StringLiteral { value: &'a str },
  Unexpected,
}

#[derive(Debug)]
pub struct Token<'a> {
    token_type: TokenType<'a>,
    lexeme: &'a str,
    start_pos: usize,
    size: usize,
}

impl<'a> Token<'a> {
    pub fn new(token_type: TokenType<'a>, lexeme: &'a str, start_pos: usize, size: usize) -> Token<'a> {
        Token { token_type, lexeme, start_pos, size }
    }
}

impl Cursor<'_> {
    fn advance_token(&mut self) -> Token {

        let start_pos = self.cursor_pos();
        let c = self.bump().unwrap();

        let token_type = match c {
            '=' => TokenType::Equal,
            '"' => self.string(),
            _ => TokenType::Unexpected,
        };

        let end_pos = self.cursor_pos();    // comment this to remove compile-error
        // let end_pos = self.cursor_pos().cloned();    // comment this to remove compile-error
        // let end_pos = 10;    // uncomment this to run successfully

        let size = end_pos - start_pos;
        let lexeme = "a"; // self.substring(start_pos, end_pos);

        Token::new(token_type, lexeme, start_pos, size)
    }

    fn string(&mut self) -> TokenType {
        let start_pos = self.cursor_pos();
        self.eat_while(|c| c != '"');
        let end_pos = self.cursor_pos();

        TokenType::StringLiteral { value: self.substring(start_pos, end_pos) }
    }

    fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) {
        while predicate(self.first()) {
            self.bump();
        }
    }
}

fn main() {
    let source = "a = \"123\"";
    let mut lexer = Cursor::new(source);

    let tok = lexer.advance_token();
    println!("{:?}", tok);
    let tok = lexer.advance_token();
    println!("{:?}", tok);
    let tok = lexer.advance_token();
    println!("{:?}", tok);
    let tok = lexer.advance_token();
    println!("{:?}", tok);
    let tok = lexer.advance_token();
    println!("{:?}", tok);
}

错误:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
  --> src/main.rs:81:23
   |
70 |     fn advance_token(&mut self) -> Token {
   |                      - let's call the lifetime of this reference `'1`
...
77 |             '"' => self.string(),
   |                    ---- mutable borrow occurs here
...
81 |         let end_pos = self.cursor_pos();    // comment this to remove compile-error
   |                       ^^^^ immutable borrow occurs here
...
88 |         Token::new(token_type, lexeme, start_pos, size)
   |         ----------------------------------------------- returning this value requires that `*self` is borrowed for `'1`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

1 个答案:

答案 0 :(得分:1)

您是否有什么特别的原因只想将词法字符串引用到源代码?看起来您无论如何都在推进字符并在“吃”时碰撞时抛出返回的拥有对象。一种更惯用的方法是将这些拥有的值与以下内容一起使用:

fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) -> String {
        let mut string = String::new();
        while predicate(self.first()) {
            write!(string, "{}", self.bump().unwrap());
        }
        string
}

Playground