soarjay
soarjay

Reputation: 651

Rust borrowing compilation errors

I'm trying to learn Rust by writing a simple lexer. Here is what I have so far...

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());

}

However when I try to compile it I get the following two errors:

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

and

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

A Google of the first error reveals that Clone()-ing is a possible solution for this kind of error, but I believe this won't work in this case as the iterators state needs to update every time next() is called.

Does anyone have any suggestions on how to overcome these issues and get it to compile?

Upvotes: 1

Views: 105

Answers (1)

Shepmaster
Shepmaster

Reputation: 430396

Ultimately, you are attempting to store a value and a reference to that value in the same struct. Unlike other formulations, this specific case allows you to "tie the knot" of references, but probably doesn't do what you want. For example, after calling init, you will never be able to move the Lexer because moving it would invalidate the reference.

It also explains the "borrow again" error. Because the lifetime is applied to self, and it's a mutable reference, the lexer itself will keep the mutable reference forever, meaning nothing else can ever mutate it, including itself.

The short answer is: don't organize your code that way. For whatever reason, parsing and lexing is a popular problem in the Rust community. Check out how other libraries do it.

Or check out how iterators work in general. The item being iterated over stays in place, and a separate iterator is returned that references the original item.

Splitting your code into the same two pieces will likely be the best direction.

Upvotes: 2

Related Questions