std::sync::mpsc::Receiver - check for data but not take

Greetings much wiser and elder Goblin leaders,

Question: How does one simply check for the existence of available data on a std::sync::mpsc::Receiver without actually taking the value?

I have a simple mpsc channel rx and tx

let (tx, rx) = std::sync::mpsc::channel();

I know I can

  1. Block and take, using rx.recv(&self) -> Result<T, RecvError>
  2. Try to take, using rx.try_recv(&self) -> Result<T, TryRecvError>

However, I don't see an option to simply "check but not take" - both options literally take available data or error.

A peek type of option would suffice - but even just a bool that indicates there's "something there right now this very instant, but I didn't take it off the queue".

Searching for this (here as well as google) did not prove fruitful, or anything I recognized as being an answer at least. I imagine this is such a simple and common question I probably am not searching the right keywords.

My use case, is that I need to implement an FFI callback that needs this type of behavior. I am using this channel type, to tx/rx data between the unsafe FFI callback and my main rust code - to keep all of that as separate as possible. ( It's also async - so I'm trying to communicate from sync to async - and I can, but this "check for data" is my last piece...)

I'm sure I could create a second channel for ffii only - but that seems heavy handed simply because there's no option to check for data (without actually taking it).

You can write your own wrapper to implement peeking:

use std::sync::mpsc;

struct PeekableReceiver<T> {
    rx: mpsc::Receiver<T>,
    peeked: Option<T>,

impl<T> PeekableReceiver<T> {
    fn peek(&mut self) -> Option<&T> {
        if self.peeked.is_some() {
        } else {
            match self.rx.try_recv() {
                Ok(value) => {
                    self.peeked = Some(value);
                Err(_) => None
    fn try_recv(&mut self) -> Result<T, mpsc::TryRecvError> {
        if let Some(value) = self.peeked.take() {
        } else {

(Note that peeking/checking would be hazardous in a multi-consumer channel, since some other thread could take the value between the check and the actual recv. But mpsc has one receiver by design, so this is well-defined.)


If you used crossbeam_channel::unbounded instead of std::sync::mpsc::channel, their Receiver has an is_empty method. (Their Receiver is also cloneable, so make sure not to clone it anyways if you want to make sure the information that the channel is still non-empty remains true, in light of the possibility that other clones of the same Receiver could take away the message.)


Wow, thank you elders.

Technically both of these solve my problem but I can only pick one so I went with the vanilla option. and thanks for the forewarn on the race condition. Although as you say might not be the case for me here, I might venture there and it's good to know.

But crossbeam seems a perfectly fine solution as well. And same, thank you for the warning about the cloning information/warning.