How to build selectable abstractions on top of channels?

Hi! I've been using crossbeam_channel recently to implement a language server, and I feel like I am repeatedly hitting a fundamental problem. Namely, I don't seem to be able to hide channels behind some abstraction while maintaining the possibility of the select.

Here are two specific instances of the problem.

In the first case, I have a Receiver<Thing> which are produced by a thread, and I know that the only reason for that thread to stop is if it has died with a panic. So, I can write a fn recv_thing(&mut self) -> Result<Thing> method, which recieves a message from a channel and, in case of null, joins the thread and propages the panic as an Err (code). The problem here is, of course, is that this is not selectable, and, naturally, I have a case where I need to select over this event and another channel. The way I solve this is by exposing the underlying channel, and invoking the thread cleanup code manually at the call site, which seems suboptimal.

The second case I've hit right now, when trying to make my server generic. I have a Receriver<ProtocolMessage>, and I want the generic part to handle the exit message, while forwarding all other messages to some specific implementation. I need to expose raw channel to the implementation, because it needs to be able to select over "a new network message / background job finished" events. But, if I give it a raw channel, I force the impl, and not the generic part, to contain code for handling the exit message.

Looks like I need some kind of monadic interface over channels, so that I can map/filter/flat_map the events, while keeping them selctable? Is it possible to achieve with the current API?

cc @anon15139276

Yep, I think that'll work. For the first use-case, I'll have something like:

fn read_message(&self, cb: FnOnce<Result<Msg, Err>>) -> SelectCase {
    SelectCase::recv(&self.chan, |msg| match msg {
        Some(msg) => cb(Ok(msg)),
        None => cb(Err(self.join_thread_and_get_panic())),
    })
}

For the second one, I'll have

fn receive_message(&self, cb: FnOnce(Option<RawMsg>)) -> SelectCase {
    SelectCase::recv(self.chan, |msg| match msg{
         None | Some(msg) if msg.method == "exit" => cb(None)
         Some(msg) => cb(Some(msg))
    })
}