Заимствования Rust и циклы
Я делаю токенизатор и столкнулся с некоторыми трудностями. Для лучшего понимания проблемы немного опишу состовляющие токенизатора:
- Токен. Бывает 3 типов - Константный (фиксированный, это ключевые слова. Мы всегда знаем их размер, текстовое содержание, оно постоянно), Литеральный и Идентификатор (и кажется из-за него проблемы). Вот реализация в программе:
#[derive(Clone, Copy)]
pub enum TokenType { //просто перечисление для обозначения типа токена
Id,
Algo, Struct, While,
Int, Float,
}
pub trait Token { //каждый тип реализует этот интерфейс
fn get_type(&self) -> TokenType;
fn get_str(&self) -> &str;
}
//константный знает строковое представление каждого типа заранее
pub struct ConstToken {
kind: TokenType
}
impl Token for ConstToken {
fn get_type(&self) -> TokenType {
self.kind
}
fn get_str(&self) -> &str {
match self.kind {
TokenType::Algo => "алгоритм",
TokenType::Struct => "структура",
TokenType::While => "покуда",
_ => panic!()
}
}
}
impl ConstToken {
pub fn new(kind: TokenType) -> Self {
ConstToken { kind }
}
}
//литеральный. помимо типа хранит текстовое представление
pub struct LitToken {
kind: TokenType,
txt: String
}
impl Token for LitToken {
fn get_type(&self) -> TokenType {
self.kind
}
fn get_str(&self) -> &str {
&self.txt
}
}
impl LitToken {
pub fn new(kind: TokenType, txt: String) -> Self {
LitToken { kind, txt }
}
}
//моя попытка сэкономить.
pub struct IdToken<'a> {
txt: &'a str
}
impl<'a> Token for IdToken<'a> {
fn get_type(&self) -> TokenType {
TokenType::Id
}
fn get_str(&self) -> &str {
self.txt
}
}
impl<'a> IdToken<'a> {
pub fn new(txt: &'a str) -> Self {
IdToken { txt }
}
}
- Функция токенизации. Я не использую regex, работаю c потоком символов с помощью циклов. Поскольку я ожидаю большое число одинаковых идентификаторов в исходнике, то идентификатор хранит &str, ссылку на единственную копию имени идентификатора. Вот код, реализующий это поведение:
//объявление функции. src - исходник. tokens и ids - список токенов и множество идентификаторов. прокидываю снаружи. пытался создавать внутри функции и возвращать, но ошибок было еще больше.
pub fn tokenize<'a>(src: &'a str, tokens: &'a mut Vec<Box<dyn Token + 'a>>, ids: &'a mut HashSet<String>)
...
loop {
let (c, i) = match iter.next() {
Some(res) => (res.1, res.0),
None => {
//если исходник кончился
let str_id = &src[first..]; //подстрока идентификатора
if !ids.contains(str_id) { //если такого Id нет, то добавляю
ids.insert(str_id.to_string()); //ЗДЕСЬ ОШИБКА КОМПИЛЯТОРА!
}
//создаю новый токен идентификатора. получаю ссылку на строку из хранилища
//хранилище - HashSet<String>
let token = IdToken::new(ids.get(str_id).unwrap());
tokens.push(Box::new(token));
break;
}
};
// тоже самое, только в случае конца идентификатора
// согласно моим правилам
if !c.is_alphabetic() {
let str_id = &src[first..];
if !ids.contains(str_id) {
ids.insert(str_id.to_string()); //ЗДЕСЬ ОШИБКА КОМПИЛЯТОРА!
}
let token = IdToken::new(ids.get(str_id).unwrap());
tokens.push(Box::new(token));
break;
}
}
В строках ids.insert(str_id.to_string());
компилятор ругается:
cannot borrow
*ids
as mutable because it is also borrowed as immutable due to object lifetime defaults,Box<dyn Token>
actually meansBox<(dyn Token + 'static)>
не очень понимаю, в чем ошибка и как ее решить.
Буду очень благодарен за помощь.
Ответы (1 шт):
Автор решения: Kalashnikov Alexander
→ Ссылка
Проблему можно решить,отвязав ссылку на ids
внутри if
let not_contains = !ids.contains(str_id);
if not_contains {
ids.insert(str_id.to_string());
}