[Solved] Trouble with Peekable and SkipWhile structs


#1

Hi all,

Presently I’m trying to iterate through some Chars that I have in something like:

use std::iter::{Iterator, Peekable};
use std::str::Chars;
use std::string::String;

fn main() {
    let s = String::from("\"hell\\\"o_world\"=>\"value\"");
    let mut chars = s.chars()
        .skip_while(|c| match *c {
            '"' => true,
            _ => false,
        })
        .peekable();
    let mut key = String::new();
    // let mut value = String::new();
    collect_and_unescape(&mut key, &mut chars);
    chars = chars.skip_while(|c| match *c {
        '"' => false,
        _ => true,
    }).peekable();
    chars.next();

    println!("key: {}", key);
}


fn collect_and_unescape<T>(value: &mut String, chars: &mut Peekable<T>)
where
    T: Iterator<Item = char>,
{
    loop {
        let next = chars.next();
        let next_char = match next {
            Some('\\') => {
                match chars.peek() {
                    Some(&'"') | Some(&'\\') => chars.next(),
                    _ => None,
                }
            }
            Some('"') | None => break,
            _ => next,
        };
        if let Some(c) = next_char {
            value.push(c);
        }
    }
}

But when I compile it, I get

❯❯❯ rustc test.rs
error[E0308]: mismatched types
  --> test.rs:16:13
   |
16 |       chars = chars.skip_while(|c| match *c {
   |  _____________^
17 | |         '"' => false,
18 | |         _ => true,
19 | |     }).peekable();
   | |_________________^ expected struct `std::str::Chars`, found struct `std::iter::Peekable`
   |
   = note: expected type `std::iter::Peekable<std::iter::SkipWhile<std::str::Chars<'_>, [closure@test.rs:8:21: 11:10]>>`
              found type `std::iter::Peekable<std::iter::SkipWhile<std::iter::Peekable<std::iter::SkipWhile<std::str::Chars<'_>, [closure@test.rs:8:21: 11:10]>>, [closure@test.rs:16:30: 19:6]>>`

error: aborting due to previous error(s)

And I’m not entirely certain how to work past it. Perhaps I’m misunderstanding how skip_while and peekable work? Any help would be appreciated.

Thanks in advance :tada:!


Type issues with closures, traits, and where clauses
#2

The fix is to simply rebind chars, i.e.:

let mut chars = chars.skip_while(|c| match *c {
        '"' => false,
        _ => true,
    }).peekable();

Note the let mut chars = ... instead of chars = ...

The reason is your original chars binding is a “layered” type that’s printed in the “expected” part of the message:
std::iter::Peekable<std::iter::SkipWhile<std::str::Chars<'_>, [closure@test.rs:8:21: 11:10]>>. This is how combinators/adapters work - the skip_while function creates the SkipWhile type, and then peekable on top of that “wraps” it into a Peekable<SkipWhile<...>, .... Note that this becomes the type of the ‘chars’ binding.

Then the code adds more combinators/adapters to the type cake because now it calls skip_while and peekable on the previous type, and you end up with a “nested” Peekable<SkipWhile<Peekable<SkipWhile<...>>>>> that the compiler says it received. So, each time you apply an adapter/combinator to iterators you get a different type back.

By rebinding the chars binding you “reset” the type because you’re creating a new type binding (you can also, of course, just call it chars2, for example).


#3

Oh wow. That’s so obvious that I feel incredibly naive. :laughing:

Thanks so much!


#4

No worries! Type inference makes the code look so nice and clean, yet there are big hairy type dragons lurking behind the scenes - compiler typically hides them from you … until it doesn’t and then you have to slay it :slight_smile:


#5

For that particular code you might want to use peeking_take_while, it exists in two implementations: itertools and peeking_take_while.