I'm interested in peeking ahead in a character stream. To my understanding, Peekable would be the way to go. I can't quite figure out how to use it.
First attempt:
fn trawl<I, E>(pk: &mut I) where I: std::iter::Peekable<Result<char, E>> {
loop {
let cur = pk.next();
let nxt = pk.peek();
match (cur, nxt) {
(Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
_ => (),
}
}
}
fn main() {
trawl(&mut std::io::stdio::stdin().chars());
}
This fails to compile with
> rustc /tmp/main.rs
/tmp/main.rs:1:37: 1:73 error: `std::iter::Peekable` is not a trait
/tmp/main.rs:1 fn trawl<I, E>(pk: &mut I) where I: std::iter::Peekable<Result<char, E>> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Okay, fair enough. I don't fully understand traits yet so I try to pass an iterator in and then create a peekable version:
fn trawl<I, E>(it: &mut I) where I: Iterator<Result<char, E>> {
let mut pk = it.peekable();
loop {
let cur = pk.next();
let nxt = pk.peek();
match (cur, nxt) {
(Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
_ => (),
}
}
}
fn main() {
trawl(&mut std::io::stdio::stdin().chars().peekable());
}
This fails with
> rustc /tmp/main.rs
/tmp/main.rs:2:18: 2:20 error: cannot move out of dereference of `&mut`-pointer
/tmp/main.rs:2 let mut pk = it.peekable();
^~
/tmp/main.rs:7:65: 7:70 error: cannot move out of dereference of `&`-pointer
/tmp/main.rs:7 (Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
^~~~~
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
/tmp/main.rs:7:39: 7:77 note: expansion site
error: aborting due to 2 previous errors
Could someone explain:
- why Peekable couldn't appear in the function type for lack of being a trait,
- what the compiler means when it says 'move out of dereference of' and
- how I might resolve either or both?
A third version
fn trawl<I, E>(mut it: I) where I: Iterator<Result<char, E>> {
let mut pk = it.peekable();
loop {
let cur = pk.next();
let nxt = pk.peek();
match (cur, nxt) {
(Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
// (Some(i), ) => println!("{}", i.ok()),
_ => (),
}
}
}
fn main() {
trawl(std::io::stdio::stdin().chars().peekable());
}
This fails with:
> rustc /tmp/main.rs
/tmp/main.rs:7:65: 7:70 error: cannot move out of dereference of `&`-pointer
/tmp/main.rs:7 (Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
^~~~~
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
/tmp/main.rs:7:39: 7:77 note: expansion site
error: aborting due to previous error
I fail to understand what rust is saying to me here, how Iterator.next would have a different return type from Peekable.peek.
Peekable
is not a trait and thus cannot be used as a bound, which would suggest that it could mean one of many types. It is a single, specific, concrete type,struct Peekable<A, T>
. As you have observed, it’s constructed by calling thepeekable()
method on an iterator, which changes it to something that is peekable.Here’s how you’d use it if you just wanted to take an iterator:
Note also that the
peekable()
method takes self by value; you can’t take a mutable reference to an iterator there.The alternative which is what you were aiming for but which I would be generally less inclined towards, would be to require the argument to be peekable, putting the burden onto the caller, as you had:
Peekable
is actually a struct, not a trait. If you wanted to take aPeekable
, you could define your function like this:Your second implementation is failing to compile because
peek
takesself
by value (i.e. it consumes the iterator, returning a new one), so you can't call it through a&mut
reference. Most code simply takes the iterator by value instead of by reference:If you don't want to move the iterator into a function like
trawl
, you can use theby_ref()
method to create a new iterator that holds onto an&mut
reference:As far as style goes, I would say that the second form is the better way to go, as the first leaks what's basically an implementation detail.