5 ms·
Absolutely, and IMHO the Mutex/MutexGuard API is one of the best showcases of what Rust is capable of. (Fun fact: RwLock<T> is Sync only if T is Sync, but Mutex
by oconnor663 4y ago
Absolutely, and IMHO the Mutex/MutexGuard API is one of the best showcases of what Rust is capable of. (Fun fact: RwLock<T> is Sync only if T is Sync, but Mutex<T> is Sync even if T is not Sync!)
- masklinn 4y ago> Fun fact: RwLock<T> is Sync only if T is Sync, but Mutex<T> is Sync even if T is not Sync! What’s more interesting is figuring out why that is. Also why Arc<T> is Send only if T is Send.
- delian66 4y agoWhy is that?
- yakubin 4y agoMy guess is one way it could break, if it was otherwise, would be if T relied on thread local state.
- masklinn 4y agoThe alternative would be to actively undermine Rust's type system (and guarantees): - RwLock hands out multiple references (that's the point), Sync means a type can be used from multiple threads (concurrently), if RwLock<T: !Sync> was Sync it would allow multiple outstanding references for the same !Sync object, which is not legal. - Mutex, however, only hands out a single reference at a time (which is also why it can always hand out a mutable reference), meaning semantically it acts like it moves the T to the target thread then borrows it = nothing to sync, that's why Mutex<T> is Sync if T is Send. - For Arc, if it were Send without the wrapped object being Send it would allow Send-ing !Send objects: create an Arc<T>, clone it, move the clone to a second thread, drop the source, now you can try_unwrap() or drop() the clone and it'll work off of the second thread. This is a problem with threadlocal state, but also with resource affinity (e.g. on windows a lock's owner is recorded during locking, and only the owner can release the lock, there are also lots of APIs which can only work off of the main thread.), or thread-safety (Rc for instance would be completely broken if you could Send it, as the entire point is to use an unsynchronised refcount).
- tialaramex 4y agoCertainly it's interesting that Mutex<T> is a thing in Rust but the equivalent (a mutex in the form of a wrapper type) doesn't exist in C++. One rationale is that Mutex<T> in Rust is actually safe, whereas the C++ equivalent would be an attractive nuisance since it looks safe but would be easily abused and if any of the mutex's users abused it you're screwed.
- smallstepforman 4y agoTo use the articles example, there are typically multiple bank accounts you want to update atomically, so guarding one account with a mutex doesn’t help you prevent deadlocks. The lock needs both accounts. The Mutex<T> example just doesn’t work with interacting objects.
- tialaramex 4y agoIn C++ you'd want to still also offer std::mutex because C++ doesn't have Zero Size Types, so a C++ Mutex<T> equivalent would always need space to store something. Mutex<()> is the same size as a hypothetical "mutex only" type and so Rust has no reason to offer a separate type representing a mutex which doesn't protect anything in particular. In fact even without actually putting anything of substance in the mutex, you can get value from type system judo using this mechanism, which C++ doesn't appear to do either.
- gpderetta 4y ago> C++ doesn't have Zero Size Types [[no_unique_address]] since C++20. Before that there was the empty base class optimization.
- tialaramex 4y agoNeither the Empty Base Class nor [[no_unique_address]] give C++ Zero Size Types. The [[no_unique_address]] attribute is a way to achieve something empty base classes were useful for without the accompanying problems, so that's nice, but it's not ZSTs. Can you say whether you genuinely thought C++ had ZSTs? And if so, how you came to that conclusion ?