This is actually a pre-RFC written in the form of a blog post:
A well written proposal and I fully agree with deprecating the mpsc module for the channel module. The transition would be pretty easy. Simply changing mpsc::channel
to channel::unbounded
and mpsc::sync_channel
to channel::bounded
for most users with the additional benefit of making your code more readable. It feels like we can have our cake and eat it by having a multiple producer multiple consumer channel with a better performance than the current multiple producer single consumer channel while being able to leave the confusing terminology behind.
Is omitting any form of selection clearly the right thing?
I remember first looking at mpsc after 1.0 and being very surprised that selection was missing (it feels like one of the basic primitives: Occamâs ALT).
From the history it looks more like select!
was omitted from 1.0 because it was unfit for stabilisation, rather than because anyone positively thought that channels without selection make a coherent feature which is worth including in a standard library.
Maybe itâs worth looking to see what proportion of crossbeam-channel
's users use selection.
I think we should deprecate mpsc long-term. Channels without select are a bad interface, because they are not composable.
I also think that, long-term, channels should be in std, because they are such a core primitive. However, I am not sure that we are ready for std::sync::channel right now. I think it perhaps makes sense to wait for async/await to stabilize, to be confident that sync::channel works both in blocking and in non-blocking contexts.
Is improving mpsc performance really beneficial? Perhaps adding a note to the docs âuse crossbeam-channelâ would be more helpful for end users?
OTOH, if we can decrease the maintainence burden of mpsc by simplifying code, that seems like a good idea. So perhaps we can introduce nightly only sync::channel with the right interface, and make mpsc a shim on top of that? We can than move sync::channel to stabilization in the future, together with the Stream abstraction.
Replacing of the existing implementation with a slightly smaller, better version seems like a definite improvement to me. If the missing Sync
could be added without breaking things, thatâd be even better.
Iâm not sure about deprecation and replacement.
mpsc
works OK for basic cases. Iâve used it in a few places and never actually needed select, so to me mpsc
isnât broken.
If we agree that mpsc
shouldnât have been in the stdlib in the first place, then adding another channel
module just repeats the mistake. Weâre already sending people to use 3rd party crates for relatively basic things, so I donât think proper channels have to be in the stdlib. The stdlib doesnât have queues (rayon), which I consider a more common and more basic primitive.
And excellent points about inconsistent naming. Thatâs something we can mostly fix right away by updating the docs and examples.
I agree with the deprecations, but once we put something in standard, we can never remove it.
If we add a different channel API and implementation to the standard library, and in 5-10 years time it turns out that itâs also âbadâ, then that would be two deprecated channel implementations that we would have to maintain forever.
I agree that channels are a core concurrency primitive, but so are many things in many fields (like, rayon
is a core parallelism primitive, but thatâs not enough of a reason to move rayon
into the standard library). The question is whether channels are a core standard library primitive. That is, whether they are a vocabulary type / trait, or whether it is impossible to implement them in stable Rust efficiently because some compiler magic is required (think: Atomic
types).
AFAIK these APIs can be implemented without issues outside the standard library. We already made this mistake once, letâs learn from it and not make it again. So my vote here is for no, letâs not move crossbeam channels into the standard library.
I wouldnât be opposed for the deprecation message to recommend users to use crossbeam channels directly (as opposed to some more generic deprecation message that does not recommend any particular library).
EDIT: also another downside could be having to maintain two different channel implementations if crossbeam and the standard library diverge. I donât know whatâs our manpower for this, but if the issue is one of discoverability, or that the Rust book has a policy of only using libstd
primitives, then we should improve those instead and try to keep libstd lean.
At first after reading this blog post I was really excited about getting a new std::sync::channel
library that contains the core of crossbeam-channel, but in the end I think I agree with @gnzlbg that it might make more sense to deprecate std::sync::mpsc
and point to the crossbeam-channel project directly. And if we do decide to deprecate it, itâs not even clear it makes sense to spend the effort to simpler channel
implementation for the current std API.
Another argument that @gnzlbg didnât mention is that itâs clear that a lot of evolution has happened already between the release of std::sync::mpsc
and the current iteration of crossbeam-channel, and itâs not clear that crossbeam-channel has matured enough (in the sense of the pace of change slowing down â not in the sense of being mature) that it would be a good fit for the âforever frozenâ promise we have for std.
Iâm not sure if we already have clearly defined guidelines for what kinds of things should be allowed to go in std, but âcore primitivesâ, âcompiler magic is requiredâ might be interesting first steps to define something like that.
If you want to argue that the crossbeam-channel library contains fundamentals that lots of projects need, I think we should also consider making chrono part of std (610k vs 740k recent downloads). By which I mean, thatâs a slippery slope and not a very clear-cut argument IMO.
Speaking as somebody relatively new, the common guidance in the community is âjust use crossbeamâ. I donât think thatâs a bad thing, since, as seen already, ânever breakingâ APIs are really really hard to get right. Thereâs a high bar to be included in stdlib, and frankly I think it should be higher than it is.
The current stdlib implementation does work for what it is, and those who care can find (and we can help them) better options.
Think about how long some boost libraries in C++ took to be included in their standard library.
From a surface level, it sounds like deprecation with a pointer to alternatives is better than the confusion of two implementations in stdlib (to me, that wouldnât inspire much confidence - though itâs not unheard of in other languages, javaâs vector/arraylist comes to mind). Iâm all for fixing the current implementation in whatever way we can though.
My humble opinion here is: replacing the guts of mpsc without changing the interface doesnât prevent the deprecation or switchover later on. If itâs already (mostly) written, it might be the first step no matter if the deprecation happens later on or not. And much faster than agreeing on new naming, if it should be deprecated, etc.
Iâm not sure how far we can get with fixing the current API with soft-renaming (keeping the old one deprecated and hidden from docs, calling the new one from within it) methods and such. It would be nice if we didnât have two eg. receiver types that would be incompatible and if mpsc and channel were actually only aliases and the types were completely the same.
Are you arguing that we should fix it even though we deprecate it?
I don't think that is a good use of resources, but I'm not going to complain if someone decides to do that.
Besides batteries-included availability, are there any special advantages for including channels in std
? For example, are there improvements that could be made with unstable features?
If a new API is provided, it would probably make sense to consider trying to align it with the futures channel API: https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures/channel/mpsc/index.html
futures-channel::Sender
will still require a function that does the current operation of disconnect
to satisfy other traits it implements, specifically Sink
requires a way to âcloseâ the sink from a mutable reference.
Other than that, I think simplifying the API as much as possible and standardising it across implementations would be a great idea.
This is actually really interesting to me. Channels being a basic primitive are one thing, but there's no reason not to leave them in a crate. But other parts of std benefitting from channels, or a desire to standardize channels across crates (so they can talk to each other with channels) are two good reasons to include it. We should be absolutely sure we can provide a good, general, performant, and stable interface and implementation though.
It sounds like replacing the guts of mpsc and deprecating it both have 0 downsides. I'd like to know if we can use channels more thoroughly in std (just for tests seems kind of meh).
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.