3

I have a simple parser that can be distilled to something like this:

use std::str::Chars;
use std::iter::Peekable;

struct Parser<'a> {
    input: Peekable<Chars<'a>>,
}

impl<'a> Parser<'a> {
    fn new(input: &'a str) -> Self {
        Parser {
            input: input.chars().peekable(),
        }
    }

    fn consume(&mut self, check: char) -> bool {
        // see below
    }
}

My original implementation of consume, which should take a look at the next character in the input and return true (and advance the Peekable) if it matches the passed character, was this:

fn consume(&mut self, check: char) -> bool {
    match self.input.peek() {
        Some(c) if *c == check => {
            self.input.next();
            true
        },
        _ => false
    }
}

The compiler informed me that I can't mutably borrow self.input for the call to next because I've already borrowed it in the call to peek:

error[E0499]: cannot borrow `self.input` as mutable more than once at a time
  --> src/main.rs:18:17
   |
16 |         match self.input.peek() {
   |               ---------- first mutable borrow occurs here
17 |             Some(c) if *c == check => {
18 |                 self.input.next();
   |                 ^^^^^^^^^^ second mutable borrow occurs here
...
22 |         }
   |         - first borrow ends here

I don't understand why adding a & to the Some match expression (and dropping the * from *c) makes the code compile:

fn consume(&mut self, check: char) -> bool {
    match self.input.peek() {
        Some(&c) if c == check => {
            self.input.next();
            true
        },
        _ => false
    }
}

Based on Boiethios' comment, what would the proper solution be if c wasn't Copy?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
  • 1
    With `Some(&c)` you drop the reference and make a copy of the char. With `Some(c)` you keep a reference on it, that prevents another borrowing. – Boiethios Dec 19 '17 at 16:54
  • @Boiethios I think I understand, thanks. What would the idiomatic solution be if `c` wasn't marked `Copy`? – Michelle Tilley Dec 19 '17 at 17:02
  • 1
    There are different solutions, but for example: `let has_matched = /*etc.*/` and after the `match` block: `if has_matched { self.input.next() }`. When you are stuck, intermediate variable is often a good solution. – Boiethios Dec 19 '17 at 17:08
  • But maybe your design does not fit the Rust paradigm. I cannot confirm that because I am not so much good at Rust, and I have no time currently to investigate this case. – Boiethios Dec 19 '17 at 17:09
  • 1
    @Shepmaster Thanks — [your answer](https://stackoverflow.com/a/47893160/62082) on the duplicated question is the explanation I was looking for (the other answers just show alternatives, which I already explored). – Michelle Tilley Dec 19 '17 at 18:48
  • @MichelleTilley yeah, I originally posted it here, but then I found that older duplicate so I moved my tweaked answer there. That helps explain why it fits so nicely with your specific questions ;-) – Shepmaster Dec 19 '17 at 18:52

0 Answers0