I am sorry to have such a long codes to show my problem. When I wrote some simplified codes, the error gones.
I want to match unary expression and primary expression. The unary expression is a operator plus unary or primary expression. For example, in !false
, the !
is a operator, false
is a primary expression, !false
is a unary expression. and the unary expression will match right side first, if my unary expression is !!!false
, the process will be:
(!!! + false) -> (!! + true) -> (! + false) -> true
I wrote recursive function to solve it, there will be more expressions in the future, and the state of process tokens (line, current position) will be saved in ParserContext
:
pub fn unary(context: &mut ParserContext) -> MyResult<Box<dyn Expr>> {
// match such as +1ã!false, and `is_match` has side effect.
if is_match(context, TokenType::Bang) || is_match(context, TokenType::Minus) {
if let Some(operate) = get_prev_token(context) {
let right = unary(context).unwrap();
// ^^^ my first question is about here
return Ok(Box::new(Unary::new(operate, right)));
// ^^^ my second question is about here.
}
}
// match literal, such as number, string, (1 + 1)
primary(context)
}
those codes got two errors, and I have two questions:
-
E0502 shows me that we should't have any other references to the variable before trying to access it mutably, but I need to do that in my situation, I want validate some conditions first, and then call
unary
recursively, I have no idea how to refactor it, so I am very curious the replacement to realize it in Rust. - I fixed the second error by adding some lifetime annotations, but I am not sure is that a right way to solve it, or maybe I should avoid write codes like this?
- pub fn unary(context: &mut ParserContext) -> MyResult<Box<dyn Expr>> {
+ pub fn unary<'a>(context: &'a mut ParserContext) -> MyResult<Box<dyn Expr + 'a>> {
pub struct Unary<'a> {
operator: &'a Token,
- right: Box<dyn Expr>,
+ right: Box<dyn Expr + 'a>,
}
impl<'a> Unary<'a> {
- pub fn new(operator: &'a Token, right: Box<dyn Expr>) -> Self {
+ pub fn new(operator: &'a Token, right: Box<dyn Expr + 'a>) -> Self {
Self { operator, right }
}
}
use std::error::Error;
type MyResult<T> = Result<T, Box<dyn Error>>;
fn main() {}
struct ParserContext {
tokens: Vec<Token>,
current: usize,
}
pub struct Token {
token_type: TokenType,
lexeme: String,
literal: String,
line: usize,
}
impl Token {
pub fn get_token_type(&self) -> TokenType {
self.token_type
}
pub fn new(token_type: TokenType, lexeme: &str, literal: &str, line: usize) -> Self {
Self {
token_type,
lexeme: String::from(lexeme),
literal: String::from(literal),
line,
}
}
}
#[derive(PartialEq, Clone, Copy)]
enum TokenType {
LeftParen,
RightParen,
Minus,
Plus,
Bang,
BangEqual,
Nil,
}
pub trait Expr {}
impl Expr for Unary<'_> {}
impl Expr for Literal {}
pub enum LiteralType {
Nil,
}
pub struct Literal {
val: String,
t: LiteralType,
}
impl Literal {
pub fn new(val: &str, t: LiteralType) -> Self {
Self {
val: val.to_owned(),
t,
}
}
}
pub struct Unary<'a> {
operator: &'a Token,
right: Box<dyn Expr>,
}
impl<'a> Unary<'a> {
pub fn new(operator: &'a Token, right: Box<dyn Expr>) -> Self {
Self { operator, right }
}
}
pub fn unary(context: &mut ParserContext) -> MyResult<Box<dyn Expr>> {
// match such as +1ã!false
if is_match(context, TokenType::Bang) || is_match(context, TokenType::Minus) {
if let Some(operate) = get_prev_token(context) {
let right = unary(context).unwrap();
return Ok(Box::new(Unary::new(operate, right)));
}
}
// match literal, such as number, string, (1 + 1)
primary(context)
}
pub fn is_match(context: &mut ParserContext, token_type: TokenType) -> bool {
match get_current_token(context).map(|token| token.get_token_type()) {
Some(t) if t == token_type => {
context.current += 1;
true
}
_ => false,
}
}
pub fn primary(_context: &mut ParserContext) -> MyResult<Box<dyn Expr>> {
unimplemented!()
}
pub fn get_prev_token(context: &ParserContext) -> Option<&Token> {
context.tokens.get(context.current - 1)
}
pub fn get_current_token(context: &ParserContext) -> Option<&Token> {
context.tokens.get(context.current)
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*context` as mutable because it is also borrowed as immutable
--> src/main.rs:82:25
|
81 | if let Some(operate) = get_prev_token(context) {
| ------- immutable borrow occurs here
82 | let right = unary(context).unwrap();
| ^^^^^^^^^^^^^^ mutable borrow occurs here
83 | return Ok(Box::new(Unary::new(operate, right)));
| ---------------------------------------- returning this value requires that `*context` is borrowed for `'static`
error: lifetime may not live long enough
--> src/main.rs:83:20
|
78 | pub fn unary(context: &mut ParserContext) -> MyResult<Box<dyn Expr>> {
| - let's call the lifetime of this reference `'1`
...
83 | return Ok(Box::new(Unary::new(operate, right)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
|
help: to declare that the trait object captures data from argument `context`, you can add an explicit `'_` lifetime bound
|
78 | pub fn unary(context: &mut ParserContext) -> MyResult<Box<dyn Expr + '_>> {
| ++++
error[E0502]: cannot borrow `*context` as mutable because it is also borrowed as immutable
--> src/main.rs:88:5
|
81 | if let Some(operate) = get_prev_token(context) {
| ------- immutable borrow occurs here
82 | let right = unary(context).unwrap();
83 | return Ok(Box::new(Unary::new(operate, right)));
| ---------------------------------------- returning this value requires that `*context` is borrowed for `'static`
...
88 | primary(context)
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to 3 previous errors