Ошибки компиляции заимствования Rust

Я пытаюсь изучить Rust, написав простой лексер. Вот что у меня пока...

use std::fs::File;
use std::io::Read;
use std::str::Chars;

pub struct Lexer<'a> {
    file_name: String,
    file_contents: String,
    iterator: Option<Chars<'a>>,
}

impl<'a> Lexer<'a> {

    fn new(fname: &str) -> Lexer {
        Lexer {
            file_name: fname.to_string(),
            file_contents: String::new(),
            iterator: None,
        }
    }


    // Reads the file contents and creates iterator
    fn init(&'a mut self) { 

        // Open the file
        let mut f = File::open(&self.file_name).expect("Couldn't open file");

        // Read the contents
        f.read_to_string(&mut self.file_contents).expect("Couldn't read file contents");

        self.iterator = Some(self.file_contents.chars());

    }

    // Gets the next character
    fn get_next(&mut self) -> Option<char> {

        self.iterator.unwrap().next()

    }

}

fn main() {

    let mut lexer = Lexer::new("test.txt");
    lexer.init();

    // Assuming the file "text.txt" contains "Hello World" 
    // then the following two lines should print "H" then "e"

    println!("{}", lexer.get_next().unwrap());
    println!("{}", lexer.get_next().unwrap());

}

Однако, когда я пытаюсь скомпилировать его, я получаю следующие две ошибки:

cannot move out of borrowed content [E0507]
main.rs:38          self.iterator.unwrap().next()

и

cannot borrow `lexer` as mutable more than once at a time [E0499]
main.rs:49      println!("{}", lexer.get_next().unwrap());

Google первой ошибки показывает, что Clone()-ing является возможным решением для такого рода ошибок, но я считаю, что в этом случае это не сработает, поскольку состояние итераторов необходимо обновлять каждый раз, когда вызывается next().

Есть ли у кого-нибудь какие-либо предложения о том, как преодолеть эти проблемы и заставить его скомпилировать?


person soarjay    schedule 04.07.2016    source источник


Ответы (1)


В конечном итоге вы пытаетесь сохранить значение и ссылку на это значение в одной и той же структуре. В отличие от других формулировок, этот конкретный случай позволяет вам "связать себя узами брака" ссылок, но, вероятно, не делает того, что вы хотите. Например, после вызова init вы никогда не сможете переместить Lexer, потому что это сделает ссылку недействительной.

Это также объясняет ошибку «одолжить снова». Поскольку время жизни применяется к самому себе, и это изменяемая ссылка, сам лексер будет хранить изменяемую ссылку навсегда, то есть ничто другое не сможет ее изменить, включая самого себя.

Короткий ответ: не организуйте свой код таким образом. По какой-то причине синтаксический анализ и лексирование — популярная проблема в сообществе Rust. Посмотрите, как это делают другие библиотеки.

Или посмотрите, как работают итераторы в целом. Итерируемый элемент остается на месте, и возвращается отдельный итератор, который ссылается на исходный элемент.

Разделение вашего кода на одинаковые две части, вероятно, будет лучшим направлением.

person Shepmaster    schedule 04.07.2016
comment
Ну, я получил половину этого. Сейчас у меня закипит чайник, чтобы сварить кофе. В следующий раз я выпью кофе, прежде чем отвечать, ха. - person Simon Whitehead; 05.07.2016