【问题标题】:cannot borrow `*self` as immutable because it is also borrowed as mutable不能将 `*self` 借为不可变的,因为它也被借为可变的
【发布时间】:2021-03-17 18:28:02
【问题描述】:

我应该如何解决这个问题,我最终做了一个可变借用,然后是一个不可变借用。

使用 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.

【问题讨论】:

    标签: rust


    【解决方案1】:

    您只想将 lexed 字符串引用到源代码是否有特殊原因?无论如何,您似乎都在处理字符,并在“吃饭”时撞到时抛出返回的拥有对象。更惯用的方法是将这些拥有的值与以下内容一起使用:

    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

    【讨论】:

    • 感谢您查看我的代码并提供解决方案。我引用 lexed 字符串的原因是为了避免拥有该字符串的副本。但看起来如果不使用不安全的生锈部分就无法做到这一点。我喜欢新的eat_while 函数如何返回它“吃”的字符串。感谢您的建议。
    猜你喜欢
    • 1970-01-01
    • 2018-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多