Unable to use futures::stream::Peekable::peek method

Hi,

I'm unable to use futures' Peekable::peek. This is on futures=0.3.0-alpha.18. Here is the general behavior im looking for.

use futures::{
    channel::mpsc, executor::block_on, future::poll_fn, prelude::*,
    stream::Peekable,
};

fn should_compile(peekable: &mut Peekable<mpsc::Receiver<i32>>) {
    block_on(poll_fn(|cx| peekable.peek(cx)));
}

But this errors with:

error[E0599]: no method named `peek` found for type `&mut futures_util::stream::peek::Peekable<futures_channel::mpsc::Receiver<i32>>` in the current scope
 --> src/main.rs:7:36
  |
7 |     block_on(poll_fn(|cx| peekable.peek(cx)));
  |                                    ^^^^ method not found in `&mut futures_util::stream::peek::Peekable<futures_channel::mpsc::Receiver<i32>>`

My guess is thats its due to peek's signature which takes a self: Pin<&mut Peekable<St> as opposed to a &mut self, but I'm not sure.

pub fn peek(
    self: Pin<&mut Peekable<St>>,
    cx: &mut Context
) -> Poll<Option<&<St as Stream>::Item>>;

The pin is your issue. Try:

Pin::new( &mut peekable ).peek(cx)

Or take it by Pin in your method. If it would be generic you could also require Unpin on the parameter, but here it isn't generic...

Using your suggestion:

fn should_compile(peekable: Pin<&mut Peekable<mpsc::Receiver<i32>>>) {
    let fut = poll_fn(|cx| peekable.peek(cx));
    block_on(fut);
}

Errors with:

error[E0507]: cannot move out of `peekable`, a captured variable in an `FnMut` closure
 --> src/main.rs:9:28
  |
8 | fn should_compile(peekable: Pin<&mut Peekable<mpsc::Receiver<i32>>>) {
  |                   -------- captured outer variable
9 |     let fut = poll_fn(|cx| peekable.peek(cx));
  |                            ^^^^^^^^ move occurs because `peekable` has type `std::pin::Pin<&mut futures_util::stream::peek::Peekable<futures_channel::mpsc::Receiver<i32>>>`, which does not implement the `Copy` trait

From what I understand this is due to the fact that futures::future::poll_fn is a FnMut which could be called multiple times, requiring moving the Pin each time. However, neither the Pin nor the &mut Peekable<...> are copyable.

Yeah, I see if it's FnMut that is probably not working. Have you tried Pin::new? pin_utils also allows pinning variables on the stack.

Yep, it yields an equivalent error since we move the &mut each time a Pin is created.

I'll take a look in futures or tokio source code to see if I can find an equivalent usage.

Hmm I see, is it clonable?

I can't clone it since my use case forces me to take a mutable reference.

Worst case scenario I can accept a &mut mpsc::Receiver<i32> and a &mut Option<i32> so that I implement the peek method by myself:

fn peek(receiver: &mut mpsc::Receiver<i32>, peeked: &mut Option<i32>) -> Option<&i32> {
    if peeked.is_some() {
        peeked.as_ref()
    } else {
        let option = block_on(receiver.recv());
        peeked.replace(option);
        peeked.as_ref()
    }
}

But that's not really a satisfactory answer.

I think the closure, returning a future means it really needs to own the data that goes into it. Especially if it needs to be Pin, since it can't be moved.

What you can do potentially is to move the poll_fn up the stack, so that this method is inside of it:

fn should_compile(peekable: Pin<&mut Peekable<mpsc::Receiver<i32>>>, mut cx: Context<'_>)
{
	let _poll = peekable.peek(&mut cx);
}

I'm not quite sure what you try to achieve, so maybe it's no help, but this will compile.

I'm not quite sure what you try to achieve, so maybe it's no help, but this will compile.

This won't work unfortunately.

I think the closure, returning a future means it really needs to own the data that goes into it. Especially if it needs to be Pin, since it can't be moved.

I do agree that the issue seems to be about the ownership of the Pin.

Looking at the futures repo, the only use of Peekable::peek is in a Future implementation, which is weirdly specific. So its possible that's its only meant to be used in this context.

I'd say it might be worth that I file a github issue on the futures github, and at least improve the function's doc.

for sure as it takes a context it's meant to be used inside a poll function. I have never used it before so I don't really know what the reasoning behind Peekable is.

Of course you can do what this test code does and create a custom future to wrap it, but it really depends on what you are trying to do.

Note that this test code looks quite old, as it doesn't pin self.inner and doesn't take self by pin, so I doubt this code still works as shown.

I agree that more explanation and examples of intended use would be helpful.

Nice, you beat me to it.

actually Nemo did, and if they didn't post an answer here, I doubt there is a good solution right now, cause they'd know.