How to peek on channels without blocking and still being able to detect hangup?
Asked Answered
I

2

6

According to the doc of .try_iter() method of the Receiver end of a Rust std::mpsc::channel, I understand that this iterator either yield "None":

  • when there is no data in the channel
  • or when the other end of the channel has hung up.

In my case, I would like to peek into the channel, without blocking, so as to determinate whether:

  • there is data (but without consuming it), or
  • there is no data, or
  • the channel has hung up.

The problem is that if I..

match my_receiver.try_iter().peekable().peek() {
    Some(data) => {/* there is data */}
    None => {/* there is no data, but there may be later.. or maybe not, because the channel has maybe hung up, I can't tell! */}
}

.. there are only two cases I can discriminate.

How can I peek into the receiver end of a channel and discriminate between those three possible outcomes without blocking or consuming the data when there is some?

Iguana answered 22/12, 2019 at 19:50 Comment(0)
E
5

The try_iter() method returns an iterator that discards the Error values, so you cannot differentiate between the cases.

You could create your own iterator which iterates over the Result rather than discarding error values:

pub struct TryIterResult<'a, T: 'a> {
    rx: &'a Receiver<T>,
}

pub fn try_iter_result<'a, T>(rx: &'a Receiver<T>) -> TryIterResult<'a, T> {
    TryIterResult { rx: &rx }
}

impl<'a, T> Iterator for TryIterResult<'a, T> {
    type Item = Result<T, TryRecvError>;

    fn next(&mut self) -> Option<Result<T, TryRecvError>> {
        match self.rx.try_recv() {
            Ok(data) => Some(Ok(data)),
            Err(TryRecvError::Empty) => Some(Err(TryRecvError::Empty)),
            Err(TryRecvError::Disconnected) => None
        }
    }
}

You would then be able to get a peekable iterator which would return three possible conditions:

match try_iter_result(my_receiver).peekable().peek() {
    Some(Ok(data)) =>{ /* there is data */},
    Some(Err(_)) => { /* nothing available right now */ },
    None => {/* disconnected */}
}    
Elizbeth answered 22/12, 2019 at 22:36 Comment(4)
You don't need the nested match: your top-level one can match directly on Some (Ok (data)) and Some (Err (_)) (playground).Hanrahan
This does work. Thank you :) Do you think there is any chance to have this integrated to the standard library?Iguana
Thanks @Jmb. I originally had it that way but got the syntax slightly wrong (see edits if you are interested).Elizbeth
@Iguana I doubt they would make a non backward compatible change like this. You could always propose it though!Elizbeth
A
-2

You can use try_recv to do what you want.

It returns a Result<T, TryRecvError> and TryRecvError can be either Empty or Disconnected.

match my_receiver.try_recv() {
    Ok(data) => {/* handle data */}
    Err(TryRecvError::Disconnected) => {/* handle sender disconnected */}
    Err(TryRecvError::Empty) => {/* handle no data available yet */}
}
Acuna answered 22/12, 2019 at 21:21 Comment(1)
try_recv will consume the data. OP wants a solution where the data is not consumed.Elizbeth

© 2022 - 2024 — McMap. All rights reserved.