Proposal: New channels for Rust's standard library


I don’t think that’s necessarily true – once can also end up with Arc<Mutex<_>> because of rayon, or something like that, and never use channels at all.

I think my preference here is just to deprecate and wait. It can develop outside of std: new test frameworks can use whatever they want, rayon seems like it’s fine outside of std, etc.


More than anything related to where they live, what I’d most like to see fall out of this opportunity is clear consistency of design across the various channel types and implementations. You get at a lot of that in the discussion about naming and terminology. I think the futures::channel and/or async case doesn’t perhaps get as much attention in the conclusion and direct proposal, and embedded channel constructs are also starting to appear. The reasons for that more-limited discussion scope are clear and fair, but the need remains.

If there is to be some minimal / base case of channels in std, one way of slicing things I haven’t seen suggested : std has only the ‘rendezvous’ or ‘zero-capacity’ implementations of channels.

  • This perhaps allows for simpler implementation (I have no idea if it really helps, and in the ways that matter most, but it seems at least plausible)
  • Perhaps even for some common traits (or at least design patterns naming conventions) that can then be shared and consistent
  • Crate-based implementations like crossbeam can add to those to include the additional complexities of buffering and bounds management, performance and contention optimisation over this shared content, etc.
  • Potentially, if this avoids allocations and GC for buffers it can even be useful for the no_std case


It sounds bad the way you state it, but I think may still be worthwhile: even after deprecation it will stay in the library ~forever. Even after deprecation it will have remaining users.


Agreed we should fix, fix & deprecate, or replace. We shouldn’t just deprecate.


Since it has to stay in the library forever, it needs to be fixed. Whether it’s deprecated in favor of a preferred replacement is a separate matter.


I think this also boils down to the same Problem other Langs have: Do we want to support those Projects officially ? Do we want to let them run and hope that the Authors keep up their good work ? For example the Apache libs for Java aren’t really a standard but it’s the defacto standard for many people.

This is also the question of what we count as “Batteries Included”, a slogan Rust had back in 2015 and I’d still count as a big plus. For example having utf-8 everywhere is kind of “batteries included”, the same goes for cargo as dependency management. But you could argue that the standard-stack of many projects are also counting to this.
I still remember being fresh to Rust and trying to write my own Error types. These days I always drop failure or error-chain in my projects, because I know that its okay to do so, that you are not taking too much overhead with that. But also because I know that these libraries are good and I’ve accepted that the std doesn’t have any solution to offer for handling errors of ~20 Dependencies, with all the benefits of failure. Sounds harsh but looking at this from the outside I can’t deny this.


Yes I can totally see the problem of removing stuff from the std that’s stabilized. ( Java has still things from 1.0 that are deprecated since then, and they won’t ever go…) But having to tell people that they should ignore the std at some parts and look for crates sounds like a trap game with an std of holes to fall into.


Not to debate the policy, but why is this? That is, why wouldn’t removal of a standard library feature be treated like any other sort of breaking change, and be permitted under certain conditions when a new epoch is released?

… I suppose even the epoch wouldn’t permit removing standard library features, since the compiler would still need to be able to build code targeting the older epochs.


Yep. This was even called out in the epochs RFC as a known limitation:

More generally, breaking changes to the standard library are not possible.


Slightly off-topic, but I’ve noticed quite a few threads suggesting using epochs to change std. If we ever choose to produce some kind of “Frequently Proposed Features” page to help avoid rehashing design discussions, we should probably include an item about this.


The phrasing is not really accurate. What we cannot do is remove things from the standard library since that would break prior editions. We could however hide things from certain editions by introducing a notion of edition visibilities. E.g. you could say pub (<= 2018) uninitialized<T>() -> T { ... }.


Note how beautifully symmetrical Sender and Receiver now are.

impl<T> Sender<T> {
    fn send(&self, t: T) -> Result<(), SendError<T>>;

impl<T> Receiver<T> {
    fn recv(&self) -> Result<T, RecvError>;

Just a random “accidental” observation, but this is “wrong”. Send and receive should not be symmetric, they should be dual, the same way as Iterator and Observer are duals:

So, the signatures should be like this:

impl<T> Sender<T> {
    fn send(&self, t: Option<T>);

impl<T> Receiver<T> {
    fn recv(&self) -> Option<T>;


You’ve answered your own question :smiley: We can “forbid” using these in new epochs, but we can’t remove them because if we do then we can’t compile older epochs. This is problematic, because many new Rust features are released for all epochs, so we have to make sure that newer Rust features on older epochs still play “nicely” with deprecated items, although it is ok for deprecated items to not fully support all new features.


I’d like to add my perspective on when channels should be in std. My thinking is that when channels are considered to be part of a good API then they should be in std. That requires then to be compostable, and it also requires that everyone agree on the same channel type, so I can use functions from two libraries that each return or accept channels, and I can use those functions together. I expect this requires select since without select any API that relies on channels is inherently limited.

My reasoning is that as long as channels are only used in executables and internally in crates there is no downside to using an external channel crate. There downside comes when you put them in an API and suddenly you require everyone else to use the same channel type. Then it seems reasonable to put the channel in std so everyone will know which channel is blessed, and that channel can further be used in the API of std itself in the same way that Result has to be in std because it is used on the API of std.