# Splitting up `std::sync` ###### tags: `Kitchen Sync` `Libs RFCs` - Feature Name: `kitchen_sync` - Start Date: 2020-08-29 - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary [summary]: #summary Split up the `std::sync` module into new `std::{arc, atomic, mutex, rwlock}` modules and take the opportunity to fix some design decisions along the way. The `std::sync` module will be deprecated in the 2021 edition, with no home for `std::sync::{mpsc, Once, Barrier}` in the new module structure. The `std::sync` module will be linted against on paths that include the `sync` module in 2018 or 2015 editions, but not on items that don't include the path. # Motivation [motivation]: #motivation The `std::sync` module is a bit of a kitchen sink; it has `Mutex` and specialized types built on it, the venerable `Arc`, the ill-fated `std::sync::mspsc` channels, atomics, and more. While many of its types work together, like `Arc<Mutex<T>>`, that doesn't necessarily mean they all belong in the same module. The non-thread-safe alternative to `Arc<Mutex<T>>`, `Rc<RefCell<T>>`, uses types from the `std::rc` and `std::cell` modules. The ergonimics of collecting everything under the `std::sync` module are less relevant today than they were when the module was stabilized with Rust `1.0`. With the new module structure in `1.0`, you would have had to write: ```diff - use std::sync::{Arc, Mutex}; + use std::arc::Arc; + use std::mutex::Mutex; let shared = Arc::new(Mutex::new(shared)); ``` but in more recent versions of Rust you can collect these two `use` statements back into one: ```diff - use std::sync::{Arc, Mutex}; + use std::{arc::Arc, mutex::Mutex}; let shared = Arc::new(Mutex::new(shared)); ``` Splitting up the `std::sync` module is an opportunity to make "breaking changes" to APIs as they're moved into new locations. For `Mutex` and `RwLock`, that means removing support for poisoning, since they're not really a concern for the locks themselves and `parking_lot` has proven there's an obvious behavior when a panic unwinds past a lock; you just unlock it. It's also a chance to remove some other APIs, like `std::sync::mpsc`, which is useful but somewhat flawed in the standard library. There are alternative channel implementations out in the ecosystem that are universally superior to `std::sync::mpsc`. The `mpsc` API won't be deprecated until it's decided whether we want to replace it. Users of `std::sync::Once` will be encouraged to use `std::lazy::SyncOnceCell<()>` instead. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation Let's say we have some state that we want to share access to. A common way to do this is to wrap it in a `Arc<Mutex<T>>`: ```rust use std::sync::{Arc, Mutex}; let shared = Arc::new(Mutex::new(data)); ``` In 2018 edition crates, the above code will generate two compiler warnings suggesting the use statement be updated: ``` use std::sync::{Arc, Mutex}; ^^^ `Arc` is moving from `std::sync` to `std::arc` consider changing this to `std::arc::Arc` ``` ``` use std::sync::{Arc, Mutex}; ^^^^^ `Mutex` is moving from `std::sync` to `std::mutex` consider changing this to `std::mutex::Mutex` ``` In 2021 edition crate, the above code will generate proper deprecation warnings. The right way to write this going forward will be: ```rust use std::{arc::Arc, mutex::Mutex}; let shared = Arc::new(Mutex::new(data)); ``` # Reference-level explanation [reference-level-explanation]: #reference-level-explanation The standard library will have the following new modules: ```rust crate core { pub mod atomic { // re-export of `core::sync::atomic` } } crate alloc { pub mod arc { // re-export of `alloc::sync::{Arc, Weak}` } } crate std { pub mod atomic { // re-export of `std::sync::atomic` .. }, pub mod arc { // re-export of `std::sync::{Arc, Weak}` Arc, Weak, }, pub mod mutex { // new `Mutex` type (it does _not_ unify with `std::sync::Mutex`) // this `Mutex` does not support poisoning Mutex, MutexGuard, Condvar, WaitTimeoutResult, }, pub mod rwlock { // new `RwLock` type (it does _not_ unify with `std::sync::RwLock`) // this `RwLock` does not support poisoning RwLock, RwLockReadGuard, RwLockWriteGuard, }, } ``` The old `std::sync` module will eventually be deprecated. A clippy lint will encourage migration to the new layout for a while before actually deprecating the `sync` module. The `#[rustc_deprecated]` attribute will need to gain support for deprecating re-exports, to pick up old usages of `std::sync::Arc`. # Drawbacks [drawbacks]: #drawbacks This will be a disruptive change to existing code. There's a _lot_ of consumers of the `std::sync` module, and they're all going to have to mess with `use` statements to avoid deprecation warnings. This only seems worthwhile as an opportunity to re-examine some early design decisions and shed some baggage. Shifting items is a single-use get-out-of-jail card, if we wanted to change things again we'd need to come up with a different scheme. Adding more top-level modules means there's more to scan through when looking for something in the standard library. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives - Why is this design the best in the space of possible designs? - What other designs have been considered and what is the rationale for not choosing them? - What is the impact of not doing this? # Prior art [prior-art]: #prior-art Discuss prior art, both the good and the bad, in relation to this proposal. A few examples of what this can include are: - For language, library, cargo, tools, and compiler proposals: Does this feature exist in other programming languages and what experience have their community had? - For community proposals: Is this done by some other community and what were their experiences with it? - For other teams: What lessons can we learn from what other communities have done here? - Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other languages. Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC. Please also take into consideration that rust sometimes intentionally diverges from common language features. # Unresolved questions [unresolved-questions]: #unresolved-questions - What parts of the design do you expect to resolve through the RFC process before this gets merged? - What parts of the design do you expect to resolve through the implementation of this feature before stabilization? - What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? # Future possibilities [future-possibilities]: #future-possibilities It might be worth considering a generic `std::panic::Poison<T>` that implements a poison guard. Not only is this a useful API in its own right, it would let users currently depending on the poisoning of `std::sync::Mutex<T>` to migrate to `std::mutex::Mutex<Poison<T>>`. The _safer transmute_ RFC demonstrates how a generic `std::atomic::Atomic<T>` type could be introduced that would continue to work with the existing `Atomic*` types. This suggests there isn't a need to deeply rethink the existing `std::sync::atomic` API in any breaking ways. The `std::sync::mpsc` module is still very useful, even if its implementation and API have some drawbacks. It's considered out-of-scope for this RFC to fix `mpsc`, but the author would welcome an RFC for `std::channel` that offered an alternative channel implementation.