Skip to content

Tracking Issue for oneshot_channel #143674

Open
@connortsui20

Description

@connortsui20

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

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,
}

Footnotes

  1. https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCS-tracking-unimplementedStatus: The feature has not been implemented.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions