rustrwlock

RwLockWriteGuard and RwLockReadGuard in same variable?


I'm currently implementing a multi client Key-Value Store (like redis or memcached) and it allows the clients to gain exclusive access to the store.

Now I have the problem that when getting values from the shared store, it can either be protected by a RwLockWriteGoard (when exclusive access is active) or a RwLockReadGuard when not.

I did not find a ways of saving the store in a variable to perform operations later on it in a way that doesn't care about whether it's protected by a read or write guard.

Here is the simplified solution I use at the moment.

// Assume Store is like this
let store = Arc::new(RwLock::new(HashMap::new()));

// --snip--
let mut exclusive_access: Option<RwLockWriteGuard<HashMap<String, String>>> = None;

while !is_finished {
    // --snip--
    let response = match parse_command(&command) {
        Command::Get(key) => {
            let read_result = match exclusive_access {
                Some(exclusive_store) => match exclusive_store.get(&key) {
                    Some(x) => Some(x.clone()),
                    None => None,
                },
                None => match store.read().unwrap().get(&key) {
                    Some(x) => Some(x.clone()),
                    None => None,
                },
            };

            // simplified
            read_result
        }
        // --snip--
    };

    if gain_exclusive_access {
        exclusive_access = Some(store.write().unwrap());
    } else {
        exclusive_access = None;
    }
}

If possible, I'd like to write the Command::Get(key) arm as something like this:

let store = match exclusive_access {
    Some(store) => store,
    None => store.read().unwrap()
};
store.get(&key)

But this doesn't work, because the two arms of that match return different Types (RwLockWriteGuard and RwLockReadGuard).

Is there a way around this, which I'm just too blind to see?


Solution

  • Use enum as tagged union.

    use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
    
    enum LockWrapper<'a, T>{
        Read(RwLockReadGuard<'a, T>),
        Write(RwLockWriteGuard<'a, T>)
    }
    
    impl<'a, T> Deref for LockWrapper<'a, T> {
        type Target = T;
    
        fn deref(&self) -> &Self::Target {
            match self {
                LockWrapper::Read(read_guard) => read_guard.deref(),
                LockWrapper::Write(write_guard) => write_guard.deref()
            }
        }
    }
    
    fn main()  {
        let lock: RwLock<i32> = RwLock::new(0);
        let condition = false;
        let guard = match condition{
            true=>LockWrapper::Read(lock.read().unwrap()),
            false=>LockWrapper::Write(lock.write().unwrap())
        };
        // Now guard holds either read or write lock.
    
    }