Search code examples
rustnonblockingchannelpeek

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


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?


Solution

  • 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 */}
    }