I understand that. (I said, "...since a RwLock wrapping a HashMap is easy to deal with...")
My point is that wrapping in a RwLock is trivial. So trivial I'm not sure I want to add another dependency to the system just to avoid that. My question is, are there other benefits to DashMap (better performance?) I'm not aware of.
Take note of all of the "Locking behaviour" warnings in the dashmap docs about deadlocks. Its API is prone to caller errors that can make it very difficult to use in complex situations. If you run into trouble, scc::HashMap is a good alternative. (It still has some deadlock warnings, but much less.)
If you need a deadlock-free concurrent map, contrie and flurry are decent.
Also, here are benchmarks for various concurrent map implementations: xacrimon/conc-map-bench
pub fn insert(&self, key: K, value: V) -> Option<V>
Inserts a key and a value into the map. Returns the old value associated with the key if there was one.
Locking behaviour: May deadlock if called when holding any sort of reference into the map.
In many cases you only access one element at a time, so this isn't a problem. But yes if you access multiple items at a time (through references, not copied) then it is a blocker.
After the nth time of encountering a deadlock with dashmap I made this repo for finding some deadlocks by forking dashmap and removing the send and sync traits from a few data structures.
This is generally good advice with any container ref type! Any wait, not just await, either, thread joins, socket io, spin locks..., so long as isn't provably going to be released by some internal mechanism that doesn't depend on the rest of the world.
That warning should really be clearer, it implies that there's a chance that you'll insta-deadlock if there happened to be any other ref held at the same time, rather than a more general it may lock waiting for some other ref to free, which you can turn into a deadlock.
Ideally the API could be designed to only allow accessing a single map entry at a time. Even a runtime check for that (only one guard allowed per thread at a time) would be better than a deadlock.
You can also use values of Arc<T> in the map and inexpensive clones instead of holding references. I don't consider the design a showstopper. It just doesn't gracefully handle naive uses.
This is no different than using a RwLock, just more implicit. You'll also end up in a deadlock if you try to mutably lock the RwLock (e.g. for inserting) while holding any reference into it.
Ultimately DashMap is just a fancy Box<[RwLock<HashMap<...>>; N]> with all its disadvantages.
What you may be missing is that the RwLocks are not visible to the user of the API. There is one per shard and there is no way to know whether two keys are in the same shard or not.
So within a single thread, if you get a ref to key X and, while this ref to X is still active, get a ref to key Y, that will self-deadlock if:
X and Y happen to be in the same shard
either X ref or Y ref is a mutable ref
This is poorly documented. It isn't discussed up front or in any general way that I can see. You have to read the Locking Behavior note for each method that accesses a key, for example get:
Locking behaviour: May deadlock if called when holding a mutable reference into the map.
But to understand it generally you really have to understand that each shard has a separate RwLock. That's not a complex concept, just poorly documented.
It's not clear to me what "it" means in when you say "It will have the same result". There were two different things (deadlocks due to reversed lock order, and self-deadlock) that they mentioned.