Search code examples
parsingrustborrow-checker

Replacing unwrap() with question mark causes borrow checker error


I have a calculator parser code set up like so:

struct Parser {
    current_index: usize,
    tokens: Vec<Token>,
}

impl Parser {
    fn new(tokens: Vec<Token>) -> Self {
        Parser {
            current_index: 0,
            tokens: tokens,
        }
    }
    
    fn consume_token(&mut self) {
        self.current_index += 1;
    }

    fn current_token(&self) -> Result<Token, &str> {
        if self.current_index >= self.tokens.len() {
            return Err("Current index out of range");
        }
        Ok(self.tokens[self.current_index])
    }
    
    fn primary(&mut self) -> Result<Expression, &str> {
        let token = self.current_token().unwrap();
        self.consume_token();
        match token {
            Token::Integer(n) => Ok(Expression::Integer(n)),
            Token::OpenParent => {
                let expr = self.expression().unwrap();
                match self.current_token().unwrap() {
                    Token::CloseParent => Ok(expr),
                    _ => Err("syntaxerr"),
                }
            }
            Token::Minus => {
                let expr = self.factor().unwrap();
                Ok(Expression::Unary(
                    Operator::Neg,
                    Box::new(expr),
                ))
            }
            _ => {
                Err("syntaxerr")
            }
        }
    }

    fn factor(&mut self) -> Result<Expression, &str> {
        let expr = self.primary().unwrap();
        let token = self.current_token().unwrap();
        match token {
            Token::Power => {
                self.consume_token();
                let rhs = self.factor().unwrap();
                Ok(Expression::Binary(
                    Operator::Pow,
                    Box::new(expr),
                    Box::new(rhs),
                ))
            }
            _ => {
                Ok(expr)
            }
        }
    }
    ...
}

I want to change the error handling by using the ? operator. I thought i can just replace the .unwrap with ?, but that causes a borrow checker error.

I tried to change the code like so:

fn primary(&mut self) -> Result<Expression, &str> {
        let token = self.current_token()?;
        self.consume_token();
        match token {
            Token::Integer(n) => Ok(Expression::Integer(n)),
            Token::OpenParent => {
                let expr = self.expression()?;
                match self.current_token().unwrap() {
                    Token::CloseParent => Ok(expr),
                    _ => Err("syntaxerr"),
                }
            }
            ...
        }
    }

That however causes a compiler error: cannot borrow *self as immutable because it is also borrowed as mutable I have read the documentation about ? operator and i don't understand why things change for the borrow checker when using ? instead of unwrap(). Any help would be appreciated.


Solution

  • Someone on reddit answered my question perfectly:

    When you result Result<T, &str>, it infers that the str borrows from &self. Instead, return Result<T, &’static str> so it knows that the str doesn’t borrow from self (or better yet, an enum that can be displayed).