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 Foowas the right move.