我应该如何解决这个问题,我最终会先进行可变借用,然后再进行不可变借用。
使用 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.
答案 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
}