Why can I modify RwLock Mutex data with only a read guard?

I've managed to modify the data of a RwLock by storing the data inside a Mutex like:

struct SqliteConn(RwLock<HashMap<String, Mutex<Option<rusqlite::Connection>>>>);

The RwLock provides me "read" access to its HashMap, but, while "reading" the HashMap I can change the values of the HashMap after obtaining a lock from the Mutex.

From the perspective of the Mutex this makes sense because a Mutex provides "interior" mutability... But... from the perspective of the RwLock this feels like unsafe mutability of its HashMap values.

Which perspective is "right"?

In case it helps, my use case is each time my business logic runs I first prefill a HashMap with empty (None) SQLite connections from a single thread and then connect every single thread to a separate db based on some shard key. So the HashMap is only ever written to from a single thread and never written to while being read from (no writer starvation possible). But many threads do simultaneously read the HashMap when they then go to connect and do work on the databases. The Mutexs for all the Connections get mutated simultaneously from multiple threads (only one thread should read/write a single Connection at a time though, but to be careful the Mutex ensures no two threads ever try to access the same Connection).

Although historically RwLock stands for Read-Write Lock, in Rust it's actually a shared/exclusive access lock since it gives out shared/exclusive references. From the perspective of the RwLock you got shared access to a Mutex, it doesn't have to be read-only though!


And that's all what matters. A well-designed API is compositional. I.e., it doesn't (and shouldn't!) matter where you got your &Mutex from. If mutating through a &Mutex is safe (and it is), then whether you got the reference from an RwLock or a static or a local variable or another, outer HashMap or… whatever, really, doesn't matter at all.

If Rust made a distinction between "&T you got from an RwLock" and "&T you got from not-an-RWLock", we would all be in deep trouble.


Thanks, it's good validation as concurrency is one of those subjects I tend to just trust the experts on. Glad Rust is so compositional. I agree, best feature of any language is ability to make things work far outside the "hello world" examples.

1 Like