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?

iago-lito
  • 3,098
  • 3
  • 29
  • 54

2 Answers2

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 */}
}    
harmic
  • 28,606
  • 5
  • 67
  • 91
  • 1
    You don't need the nested `match`: your top-level one can match directly on `Some (Ok (data))` and `Some (Err (_))` ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=398418924821ee4921958693a21a7168)). – Jmb Dec 23 '19 at 07:32
  • This does work. Thank you :) Do you think there is any chance to have this integrated to the standard library? – iago-lito Dec 23 '19 at 08:01
  • Thanks @Jmb. I originally had it that way but got the syntax slightly wrong (see edits if you are interested). – harmic Dec 23 '19 at 09:08
  • @iago-lito I doubt they would make a non backward compatible change like this. You could always propose it though! – harmic Dec 23 '19 at 09:12
-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 */}
}
FlyingFoX
  • 3,379
  • 3
  • 32
  • 49