Заимствования Rust и циклы

Я делаю токенизатор и столкнулся с некоторыми трудностями. Для лучшего понимания проблемы немного опишу состовляющие токенизатора:

  1. Токен. Бывает 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 }
    }
}
  1. Функция токенизации. Я не использую 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 means Box<(dyn Token + 'static)>

не очень понимаю, в чем ошибка и как ее решить.

Буду очень благодарен за помощь.


Ответы (1 шт):

Автор решения: Kalashnikov Alexander

Проблему можно решить,отвязав ссылку на ids внутри if

let not_contains = !ids.contains(str_id);
if not_contains {
  ids.insert(str_id.to_string());
}
→ Ссылка