Cannot use ... because it was mutably borrowed

I'm confused as to why the borrow checker isn't letting me do this:

let mut scanner = Scanner::new(script); // "script" is a &str
// scan_tokens(&mut self) -> &Vec<Token>
let tokens: &Vec<Token> = scanner.scan_tokens();

self.had_error = scanner.had_error; // "had_error" is a bool

for token in tokens {
    println!("{:?}", token);
}
error[E0503]: cannot use `scanner.had_error` because it was mutably borrowed
  --> src\lox.rs:39:26
   |
38 |         let tokens = scanner.scan_tokens();
   |                      ------- `scanner` is borrowed here
39 |         self.had_error = scanner.had_error;
   |                          ^^^^^^^^^^^^^^^^^ use of borrowed `scanner`
40 |
41 |         for token in tokens {
   |                      ------ borrow later used here

but the following seems to be fine:

let mut scanner = Scanner::new(script);

let tokens: &Vec<Token> = scanner.scan_tokens();
for token in tokens {
    println!("{:?}", token);
}

self.had_error = scanner.had_error;

I've read the book section about lifetimes but am still confused as to why the first bit of code is considered an error. I'd have thought fetching a primitive value from behind a mutable reference is okay because it's a primitive and therefore gets copied (i.e. no extra references get held).

If somebody could help me wrap my dumb brain around this and maybe point me to some learning resources that go into more depth than the book I would really appreciate it. Thanks.

1 Like

You code is missing a lot of context. What is script? What's the definition of scan_tokens()? What is the type of had_errors? Is the error about borrowing self or scanner?

If I had to take a guess scan_tokens() takes a &mut self, which means it is mutably borrowed for as long as tokens exist, even though tokens is a shared reference itself. That's because what's matter is the reference that started the lifetime, not the one currently requiring it to be alive. Not doing so would be unsound when combined with Cell::get_mut/RefCell::get_mut. Or maybe this isn't the case and I just wasted time writing this reply...

1 Like

Sorry about the lack of context. I was trying to keep the amount of code to a minimum but it sounds like I went overboard. I've edited the original post to (hopefully) be clearer.

Despite the spelling, &mut _ are exclusive references. While an exclusive borrow is active, nothing else is allowed behind the curtain -- not just no writes, but also no reads (like a copy would do).

I've written a bit about getting an intuition for borrow errors here. At a guess the parts that apply most directly to this code are...

3 Likes

Because the validity of the mutably borrowed reference to "scanner" has not yet expired (or lifecycle, as I prefer to call it; note the range of "tokens", which is the key point in determining the validity of the mutably borrowed reference to scanner), and before it expires, "self.had_error = scanner.had_error;" immutably continues to borrow from "scanner", so it reports the error.

1 Like

I would like to translate your book into Chinese and make it available to people in my country, may I ask your permission?

Feel free!

1 Like

While an exclusive borrow is active, nothing else is allowed behind the curtain -- not just no writes, but also no reads (like a copy would do).

Don't both tokens and had_error constitute behind the curtain? How come the had_error access has to come after the tokens access?

But doesn't the tokens access also constitute an immutable borrow? How come that one's okay, but the had_error one isn't?

The tokens variable is the one holding the borrow that's preventing other accesses (i.e. other than tokens). This borrow is held as long as tokens could still be used, that is until the for ends. After that the borrow is released and you're free to use scanner again, like to access scanner.had_error.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.