Aniket Navlur
Aniket Navlur

Reputation: 1012

cannot borrow `*self` as immutable because it is also borrowed as mutable

How should I solve this problem where I end up making a mutable borrow followed by an immutable borrow.

Using clone() or adding different scope is not working still getting a compile error.

I have added the complete code in the playground below: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f576dd99feec48c86e5ef4e986aefa17

Code:

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);
}

Error:

   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.

Upvotes: 1

Views: 467

Answers (1)

Sei4or
Sei4or

Reputation: 68

Is there a particular reason you only want to reference lexed strings to the source? It seems like you are progressing chars anyway and throwing the returned owned object when you bump while "eating". A more idiomatic way would be just to use those owned values with something like:

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

Upvotes: 1

Related Questions