错误[E0507]:无法移出借用的内容
error[E0507]: Cannot move out of borrowed content
我正在尝试用 Rust 制作一个词法分析器,虽然我对它还比较陌生,但有 C/C++ 的背景。我在 Rust 如何在以下代码中分配内存时遇到问题,这会生成错误 "Cannot move out of borrowed content"。我已经阅读了 cargo --explain E0507
,其中详细介绍了可能的解决方案,但我正在努力掌握 Rust 和 C/C++ 如何管理内存之间的潜在差异。本质上,我想了解如何在 Rust 中管理动态内存(或实现我正在做的事情的更好方法)。
错误是:
error[E0507]: cannot move out of borrowed content
--> <anon>:65:16
|
65 | return self.read_tok.unwrap();
| ^^^^ cannot move out of borrowed content
error[E0507]: cannot move out of borrowed content
--> <anon>:73:16
|
73 | return self.peek_tok.unwrap();
| ^^^^ cannot move out of borrowed content
error: aborting due to 2 previous errors
密码是:
use std::fmt;
#[derive(Debug, PartialEq)]
pub enum TokenType {
EndOfFile,
Illegal
}
pub struct Token {
token_type: TokenType,
value: String
}
impl Token {
pub fn new(token_type: TokenType, value: String) -> Token {
return Token {
token_type: token_type,
value: value
};
}
pub fn is_token_type(&self, token_type: TokenType) -> bool {
return self.token_type == token_type;
}
}
impl fmt::Debug for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}[{}]", self.token_type, self.value)
}
}
pub struct Lexer {
input: String,
read_pos: usize,
peek_pos: usize,
ch: char,
read_tok: Option<Token>,
peek_tok: Option<Token>
}
const EOF: char = 0 as char;
impl Lexer {
pub fn new(input: &str) -> Lexer {
return Lexer {
input: input.to_string(),
read_pos: 0,
peek_pos: 1,
ch: EOF,
read_tok: None,
peek_tok: None
};
}
pub fn next_token(&mut self) -> Token {
if self.peek_tok.is_none() {
self.read_tok = Some(self.get_next_token());
} else {
self.read_tok = self.peek_tok.take();
}
return self.read_tok.unwrap();
}
pub fn peek_token(&mut self) -> Token {
if self.peek_tok.is_none() {
self.peek_tok = Some(self.get_next_token());
}
return self.peek_tok.unwrap();
}
fn get_next_token(&mut self) -> Token {
let ch = self.next_char();
let tok: Token;
match ch {
EOF => { tok = Token::new(TokenType::EndOfFile, "".to_string()); }
_ => { tok = Token::new(TokenType::Illegal, ch.to_string()); }
}
return tok;
}
fn next_char(&mut self) -> char {
if self.peek_pos >= self.input.len() {
self.ch = EOF;
} else {
self.ch = self.input.chars().nth(self.peek_pos).unwrap();
}
self.read_pos = self.peek_pos;
self.peek_pos += 1;
return self.ch;
}
}
fn main() {
let input = "let x = 5;";
let mut l = Lexer::new(input);
loop {
let t = l.next_token();
println!("{:?}", t);
if t.is_token_type(TokenType::EndOfFile) {
break;
}
}
}
我已经设法用 C++ 翻译了一个有效的实现,它可能会提供更多关于我正在努力实现的目标的信息:
#include <string>
#include <iostream>
enum TokenType {
ENDOFFILE,
ILLEGAL
};
class Token {
private:
enum TokenType token_type;
std::string value;
public:
Token(enum TokenType token_type, std::string value)
{
this->token_type = token_type;
this->value = value;
}
bool is_token_type(enum TokenType token_type)
{
return this->token_type == token_type;
}
std::string to_string()
{
std::string tok;
switch (this->token_type) {
case ENDOFFILE:
tok = "EndOfFile";
break;
case ILLEGAL:
tok = "Illegal[" + this->value + "]";
break;
}
return tok;
}
};
class Lexer {
private:
std::string input;
int read_pos;
int peek_pos;
char ch;
Token *read_tok;
Token *peek_tok;
Token *get_next_token() {
char c = this->next_char();
std::string c_str;
Token *t;
c_str.push_back(c);
switch (c) {
case 0:
t = new Token(ENDOFFILE, "");
break;
default:
t = new Token(ILLEGAL, c_str);
}
return t;
}
char next_char()
{
if (this->peek_pos >= this->input.length()) {
this->ch = 0;
} else {
this->ch = input.at(this->peek_pos);
}
this->read_pos = this->peek_pos;
this->peek_pos += 1;
return this->ch;
}
public:
Lexer (std::string input)
{
this->input = input;
this->read_pos = -1;
this->peek_pos = 0;
this->ch = 0;
this->read_tok = NULL;
this->peek_tok = NULL;
}
Token *next_token()
{
if (this->read_tok != NULL) {
delete this->read_tok;
}
if (this->peek_tok == NULL) {
this->read_tok = this->get_next_token();
} else {
this->read_tok = this->peek_tok;
this->peek_tok = NULL;
}
return this->read_tok;
}
Token *peek_token()
{
if (this->peek_tok == NULL) {
this->peek_tok = this->get_next_token();
}
return this->peek_tok;
}
};
int main(int argc, char **argv)
{
std::string input = "let x = 5;";
Lexer l = Lexer(input);
while (1) {
Token *t = l.next_token();
std::cout << t->to_string() << std::endl;
if (t->is_token_type(ENDOFFILE)) {
break;
}
}
return 0;
}
你已经非常接近正确了,但是你的代码有两个问题。
首先,正如编译器告诉您的那样,禁止以下内容:
self.read_tok = self.peek_tok;
self.peek_tok = None;
第一行尝试将 Option<Token>
对象移出 self.peek_tok
。在 Rust 中,对象可以移出变量,但不能移出结构域或切片下标。这是因为编译器可以检查变量在移动后没有被使用,以及安排它的析构函数没有被调用。这对于存储在结构字段或切片内部的对象是不可能的,至少在不增加每个结构或容器的开销的情况下是不可能的。
只要将对象存储在支持移动的中间容器中,就可以将对象移出结构。幸运的是,Option
就是这样一个容器,它的 take()
方法正是为此目的而设计的:
self.read_tok = self.peek_tok.take()
Option::take()
从选项中移动对象,用 None
替换它,returns 对象。
其次,一旦上述问题得到解决,编译器会在 next_token
和 peek_token
的 return
语句中抱怨 "moving out of borrowed content",因为它们试图将对象移出的 Option
。在这里,您可以选择克隆 Token
,或如上所述使用 Option::take()
将其移出选项。克隆方法需要将 #[derive(Clone)]
添加到 TokenType
和 Token
,以及将 returns 更改为:
// Use as_ref() to convert Option<Token> to Option<&Token>,
// which is unwrapped and the Token inside cloned
self.read_tok.as_ref().unwrap().clone()
通过这些更改,该示例可以编译,但它仍然将输入标记为非法。
我正在尝试用 Rust 制作一个词法分析器,虽然我对它还比较陌生,但有 C/C++ 的背景。我在 Rust 如何在以下代码中分配内存时遇到问题,这会生成错误 "Cannot move out of borrowed content"。我已经阅读了 cargo --explain E0507
,其中详细介绍了可能的解决方案,但我正在努力掌握 Rust 和 C/C++ 如何管理内存之间的潜在差异。本质上,我想了解如何在 Rust 中管理动态内存(或实现我正在做的事情的更好方法)。
错误是:
error[E0507]: cannot move out of borrowed content
--> <anon>:65:16
|
65 | return self.read_tok.unwrap();
| ^^^^ cannot move out of borrowed content
error[E0507]: cannot move out of borrowed content
--> <anon>:73:16
|
73 | return self.peek_tok.unwrap();
| ^^^^ cannot move out of borrowed content
error: aborting due to 2 previous errors
密码是:
use std::fmt;
#[derive(Debug, PartialEq)]
pub enum TokenType {
EndOfFile,
Illegal
}
pub struct Token {
token_type: TokenType,
value: String
}
impl Token {
pub fn new(token_type: TokenType, value: String) -> Token {
return Token {
token_type: token_type,
value: value
};
}
pub fn is_token_type(&self, token_type: TokenType) -> bool {
return self.token_type == token_type;
}
}
impl fmt::Debug for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}[{}]", self.token_type, self.value)
}
}
pub struct Lexer {
input: String,
read_pos: usize,
peek_pos: usize,
ch: char,
read_tok: Option<Token>,
peek_tok: Option<Token>
}
const EOF: char = 0 as char;
impl Lexer {
pub fn new(input: &str) -> Lexer {
return Lexer {
input: input.to_string(),
read_pos: 0,
peek_pos: 1,
ch: EOF,
read_tok: None,
peek_tok: None
};
}
pub fn next_token(&mut self) -> Token {
if self.peek_tok.is_none() {
self.read_tok = Some(self.get_next_token());
} else {
self.read_tok = self.peek_tok.take();
}
return self.read_tok.unwrap();
}
pub fn peek_token(&mut self) -> Token {
if self.peek_tok.is_none() {
self.peek_tok = Some(self.get_next_token());
}
return self.peek_tok.unwrap();
}
fn get_next_token(&mut self) -> Token {
let ch = self.next_char();
let tok: Token;
match ch {
EOF => { tok = Token::new(TokenType::EndOfFile, "".to_string()); }
_ => { tok = Token::new(TokenType::Illegal, ch.to_string()); }
}
return tok;
}
fn next_char(&mut self) -> char {
if self.peek_pos >= self.input.len() {
self.ch = EOF;
} else {
self.ch = self.input.chars().nth(self.peek_pos).unwrap();
}
self.read_pos = self.peek_pos;
self.peek_pos += 1;
return self.ch;
}
}
fn main() {
let input = "let x = 5;";
let mut l = Lexer::new(input);
loop {
let t = l.next_token();
println!("{:?}", t);
if t.is_token_type(TokenType::EndOfFile) {
break;
}
}
}
我已经设法用 C++ 翻译了一个有效的实现,它可能会提供更多关于我正在努力实现的目标的信息:
#include <string>
#include <iostream>
enum TokenType {
ENDOFFILE,
ILLEGAL
};
class Token {
private:
enum TokenType token_type;
std::string value;
public:
Token(enum TokenType token_type, std::string value)
{
this->token_type = token_type;
this->value = value;
}
bool is_token_type(enum TokenType token_type)
{
return this->token_type == token_type;
}
std::string to_string()
{
std::string tok;
switch (this->token_type) {
case ENDOFFILE:
tok = "EndOfFile";
break;
case ILLEGAL:
tok = "Illegal[" + this->value + "]";
break;
}
return tok;
}
};
class Lexer {
private:
std::string input;
int read_pos;
int peek_pos;
char ch;
Token *read_tok;
Token *peek_tok;
Token *get_next_token() {
char c = this->next_char();
std::string c_str;
Token *t;
c_str.push_back(c);
switch (c) {
case 0:
t = new Token(ENDOFFILE, "");
break;
default:
t = new Token(ILLEGAL, c_str);
}
return t;
}
char next_char()
{
if (this->peek_pos >= this->input.length()) {
this->ch = 0;
} else {
this->ch = input.at(this->peek_pos);
}
this->read_pos = this->peek_pos;
this->peek_pos += 1;
return this->ch;
}
public:
Lexer (std::string input)
{
this->input = input;
this->read_pos = -1;
this->peek_pos = 0;
this->ch = 0;
this->read_tok = NULL;
this->peek_tok = NULL;
}
Token *next_token()
{
if (this->read_tok != NULL) {
delete this->read_tok;
}
if (this->peek_tok == NULL) {
this->read_tok = this->get_next_token();
} else {
this->read_tok = this->peek_tok;
this->peek_tok = NULL;
}
return this->read_tok;
}
Token *peek_token()
{
if (this->peek_tok == NULL) {
this->peek_tok = this->get_next_token();
}
return this->peek_tok;
}
};
int main(int argc, char **argv)
{
std::string input = "let x = 5;";
Lexer l = Lexer(input);
while (1) {
Token *t = l.next_token();
std::cout << t->to_string() << std::endl;
if (t->is_token_type(ENDOFFILE)) {
break;
}
}
return 0;
}
你已经非常接近正确了,但是你的代码有两个问题。
首先,正如编译器告诉您的那样,禁止以下内容:
self.read_tok = self.peek_tok;
self.peek_tok = None;
第一行尝试将 Option<Token>
对象移出 self.peek_tok
。在 Rust 中,对象可以移出变量,但不能移出结构域或切片下标。这是因为编译器可以检查变量在移动后没有被使用,以及安排它的析构函数没有被调用。这对于存储在结构字段或切片内部的对象是不可能的,至少在不增加每个结构或容器的开销的情况下是不可能的。
只要将对象存储在支持移动的中间容器中,就可以将对象移出结构。幸运的是,Option
就是这样一个容器,它的 take()
方法正是为此目的而设计的:
self.read_tok = self.peek_tok.take()
Option::take()
从选项中移动对象,用 None
替换它,returns 对象。
其次,一旦上述问题得到解决,编译器会在 next_token
和 peek_token
的 return
语句中抱怨 "moving out of borrowed content",因为它们试图将对象移出的 Option
。在这里,您可以选择克隆 Token
,或如上所述使用 Option::take()
将其移出选项。克隆方法需要将 #[derive(Clone)]
添加到 TokenType
和 Token
,以及将 returns 更改为:
// Use as_ref() to convert Option<Token> to Option<&Token>,
// which is unwrapped and the Token inside cloned
self.read_tok.as_ref().unwrap().clone()
通过这些更改,该示例可以编译,但它仍然将输入标记为非法。