This week I learned 15
Looking ahead
I wanted to iterate over a sequence of characters. Notably, I wanted to peek
ahead at times without advancing the iterator. The standard library has a
Peekable
iterator
to look forward without advancing the iterator. Perfect.
use std::str::Chars;
struct Foo<'a> {
chars: Chars<'a>,
}
impl<'a> Foo<'a> {
pub fn new(s: &'a str) -> Self {
Foo { chars: s.chars() }
}
pub fn bar(&self) {
if self.chars.peekable().peek().is_some() {
self.baz();
}
}
pub fn baz(&self) {
self.chars.next();
}
}
As you might imagine, this doesn’t work. peekable
takes an iterator by
value
. Foo::bar
takes self
by shared reference.
% cargo check
Checking chars v0.1.0 (/Users/nick/projects/rust/sandbox/chars)
error[E0507]: cannot move out of `self.chars` which is behind a shared reference
--> src/main.rs:13:12
|
13 | if self.chars.peekable().peek().is_some() {
| ^^^^^^^^^^ move occurs because `self.chars` has type `std::str::Chars<'_>`,
which does not implement the `Copy` trait
error[E0596]: cannot borrow `self.chars` as mutable, as it is behind a `&` reference
--> src/main.rs:19:9
|
18 | pub fn baz(&self) {
| ----- help: consider changing this to be a mutable reference: `&mut self`
19 | self.chars.next();
| ^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot
be borrowed as mutable
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0507, E0596.
For more information about an error, try `rustc --explain E0507`.
error: could not compile `chars`.
A way out
Stepping back to the original problem:
I wanted to peek ahead at times
So what I really want is an iterator
that can peek
. Sounds a lot like
Peekable
.
Since Peekable
is an iterator, it provides a next
, has_next
, etc. APIs in
addition to its ‘peek
API.
use std::iter::Peekable;
use std::str::Chars;
struct Foo<'a> {
chars: Peekable<Chars<'a>>,
}
impl<'a> Foo<'a> {
pub fn new(s: &'a str) -> Self {
Foo {
chars: s.chars().peekable(),
}
}
pub fn bar(&mut self) {
if self.chars.peek().is_some() {
self.baz();
}
}
pub fn baz(&mut self) {
self.chars.next();
}
}
This took me a long time (and a half-baked reimplementation of
Peekable
) to figure out. I was
convinced holding a Chars
iterator in Foo
was the right move.