Search code examples
rustborrow-checkerrwlock

Using RwLock.read() on a mutable struct


I'm trying to use a RwLock around a mutable struct, but I can't make it compile and I'm not sure why.

Here's a minimum code sample:

use std::sync::RwLock;
use lru::LruCache;

fn main() {
        let mut cache: LruCache<String,String> = LruCache::new(100);
        cache.put("test".to_string(), "test_value".to_string());
        let lock_cache = RwLock::new(cache);
        let rc = lock_cache.read();
        let res = rc.unwrap().get("test");
        assert_eq!(res.unwrap().as_str(), "test_value");
}

where LruCache comes from an external Rust crate (but I don't think it has a specific role in the issue). The compiler complains with this message:

error[E0596]: cannot borrow data in a dereference of `RwLockReadGuard<'_, LruCache<String, String>>` as mutable
   --> tests/cache_test.rs:295:15
    |
295 |     let res = rc.unwrap().get("test");
    |               ^^^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `RwLockReadGuard<'_, LruCache<String, String>>`

I checked the docs for RwLock and while RwLockWriteGuard does implement DerefMut, RwLockReadGuard does not.

I'm quite new to Rust so I'm pretty sure I'm doing something wrong. Is there a way I can workaround the DerefMut is required but not implemented, compiler error?

EDIT I changed the code so that is easily executable from a main file.


Solution

  • LruCache::get requires &mut self, as per its signature (docs):

    pub fn get<'a, Q>(&'a mut self, k: &Q) -> Option<&'a V> 
    

    This is because of the nature of LRU caches: in order to keep track which items were most recently used, the cache needs to modify (write) its state:

    <...> Moves the key to the head of the LRU list if it exists.

    even though you're doing a "read" operation.

    So, in your case, RwLock wins you nothing over Mutex, as you have to acquire a write lock anyway. The simplest options are either to use a Mutex<LruCache<K, V>> instead, or choose another way of caching.