Reputation: 380
I'm writing a simple tokenizer in Rust but I'm having trouble. I've simplified the code a bit for the sake of this question:
use std::iter::Peekable;
use std::str::Chars;
struct Example<'a> {
it: Peekable<Chars<'a>>,
}
impl<'a> Example<'a> {
fn tokenize_string(&mut self) {
loop {
match self.it.peek() {
None => break,
Some(_x) => self.it.next(),
};
}
}
}
The error I'm getting is:
error[E0499]: cannot borrow `self.it` as mutable more than once at a time
--> src/main.rs:13:29
|
11 | match self.it.peek() {
| ------- first mutable borrow occurs here
12 | None => break,
13 | Some(_x) => self.it.next(),
| ^^^^^^^ second mutable borrow occurs here
14 | };
| - first borrow ends here
I've been able to work around this by creating a copy of the iterator and calling peek()
on that:
fn tokenize_string(&mut self) {
loop {
let mut iterator = self.it.clone();
match iterator.peek() {
None => break,
Some(_x) => self.it.next(),
};
}
}
Is this the best way to do this? It seems a little hack-ish.
Upvotes: 4
Views: 2628
Reputation: 128051
Since you're working with str::chars()
, and char
is Copy
, you can dereference to get a char
instead of &char
. :
fn tokenize_string(&mut self) {
loop {
let r = self.it.peek().cloned();
let n = match r {
Some(_) => self.it.next(),
None => break,
};
// whatever
}
}
If you just want to check if the iterator has returned something, use is_some()
:
let r = self.it.peek().is_some();
if r { ... } else { ... }
In general, however, I'm not sure if it is possible exactly in this manner without non-lexical lifetimes. You will need to put the code which checks iterator state and the code which works with the iterator based on the state with scopes, something like this:
let r = {
// work with self.it
};
if r { ... } else { ... }
Here any references into self
must not escape the lexical block in r
, so there is no direct match
on a value which contains references into self
. There's further examples of working around this in Rust borrow of a HashMap lasts beyond the scope it's in?.
Upvotes: 3