Description
Feature gate: #![feature(oneshot_channel)]
This is a tracking issue for adding a one-shot channel to the standard library, as discussed in rust-lang/libs-team#610
Public API
Note that this API is similar to oneshot
, but is slightly different. The oneshot
crate has methods that take &self
instead of self
for fallable methods, which means that it is possible for a calller to try_recv
more than once, even if the value has already been sent.
By taking ownership of self
in every method, we guarantee that we will never have previously received a value over the channel.
This does come at the cost of ergonomics, as we need to return the corresponding end of the channel back to the caller if the method fails.
#![feature(oneshot_channel)]
pub fn channel<T>() -> (Sender<T>, Receiver<T>) { ... }
/**************************************************************************************************/
/// Note that this API is essentially identical to the `oneshot` crate.
pub struct Sender<T> { ... }
impl<T> Sender<T> {
/// Sends the value to the receiving end. This method should be wait-free.
/// Can only fail if the corresponding `Receiver<T>` has been dropped.
pub fn send(self, t: T) -> Result<(), SendError<T>> { ... }
}
/// Gives the sent value back to the caller.
/// Should this inherit from the `mpsc` module like `mpmc`?
pub struct SendError<T> { ... }
/**************************************************************************************************/
pub struct Receiver<T> { ... }
impl<T> Receiver<T> {
/// Receives the value from the sending end, blocking the calling thread until it gets it.
/// Can only fail if the corresponding `Sender<T>` has been dropped.
pub fn recv(self) -> Result<T, RecvError> { ... }
// Fallable methods.
/// Returns if the sender has sent the value over the channel.
/// Can only fail with `RecvError` if the corresponding `Sender<T>` has been dropped.
/// If this method returns `Ok(true)`, then any of the `recv` methods below are
/// guaranteed to return the value successfully without blocking.
pub fn is_ready(&self) - Result<bool, RecvError> { ... }
/// Attempts to receive the sent value without blocking.
pub fn try_recv(self) -> Result<T, TryRecvError<T>> { ... }
/// Attempts to receive the value, returning an error if it waits longer than `timeout`.
pub fn recv_timeout(self, timeout: Duration) -> Result<T, RecvTimeoutError<T>> { ... }
/// Attempts to receive the value, returning an error if `deadline` is reached.
pub fn recv_deadline(self, deadline: Instant) -> Result<T, RecvTimeoutError<T> { ... }
}
/// Should this inherit from `RecvError` in `mpsc`?
pub struct RecvError;
pub enum TryRecvError<T> {
Empty(Receiver<T>),
Disconnected,
}
pub enum RecvTimeoutError<T> {
Timeout(Receiver<T>),
Disconnected,
}
Steps / History
- ACP: ACP: Add a
oneshot
channel libs-team#610 - Implementation
- Initial Prototype:
oneshot
Channel #143741 - Add an
is_ready
method - Add an
async
API
- Initial Prototype:
- Final comment period (FCP)1
- Stabilization PR
Unresolved Questions
Right now, mpmc
's SendError<T>
is simply mpsc
's SendError<T>
(docs here). I'm guessing that this channel should also inherit the same SendError<T>
type from mpsc
?. And a similar question for RecvError
.
If we decide to always take ownership, what should happen on error for the recv
methods? Or in other words, how should users take back ownership of the Receiver
after a method has failed? Should it be encapsulated in a TryRecvError<T>
or RecvTimeoutError<T>
and extracted via an into_receiver
method, or returned as a tuple of (Receiver<T>, TryRecvError/RecvTimeoutError)
?
Edit: Resolved. Returning a tuple of (Receiver<T>, TryRecvError)
is a bad idea since if the error kind is disconnected, there is zero reason we should also return the receiver back to the caller.
//! Option 1
pub fn try_recv(self) -> Result<T, TryRecvError> { ... }
pub enum TryRecvError<T> {
Empty(Receiver<T>),
Disconnected,
}
impl<T> TryRecvError<T> {
pub fn into_inner(self) -> Option<Receiver<T>> {
match self {
Self::Empty(receiver) => Some(receiver),
Self::Disconnected => None,
}
}
}
/**************************************************************************************************/
//! Option 2
/// NOT GOOD: If `TryRecvError` is the variant `Disconnected`, then returning the `Receiver`
/// doesn't make sense.
pub fn try_recv(self) -> Result<T, (Receiver<T>, TryRecvError)> { ... }
pub enum TryRecvError {
Empty,
Disconnected,
}