I am using the leveldb crate and I am trying to get a Snapshot struct and move this into my struct but I'm having problems as I can't move a referenced value out of a function.
Here is the code for the structs and traits from the crate. I haven't included it here but the Database type implements the Snapshots trait to return a Snapshot:
pub trait Snapshots<K: Key> {
fn snapshot<'a>(&'a self) -> Snapshot<'a, K>;
}
pub struct Snapshot<'a, K: Key + 'a> {
raw: RawSnapshot,
database: &'a Database<K>,
}
And here is my code:
pub struct Cache {
ldb: RwLock<Database<i32>>
//other fields omitted
}
pub struct MySnapshot<'a> {
snapshot: Snapshot<'a, i32>
//other fields omitted
}
impl Cache {
pub fn snapshot<'a>(&'a self) -> MySnapshot<'a> {
let snapshot = self.ldb.read().unwrap().snapshot();
MySnapshot { snapshot }
}
}
I think the reason I can't do this (correct me if I'm wrong) is that with RwLock, when I get a reference to the wrapped data (Database in my case), the lifetime of that reference isn't self and so it gets dropped when the RwLock goes out of scope. One possible work around I can think of is removing the RwLock on Database and adding another field:
pub struct Cache {
ldb: Database<i32>
//The wrapped type isn't important, just need this to make the function block
ldb_rwlock: RwLock<u8>
//other fields omitted
}
impl Cache {
pub fn snapshot<'a>(&'a self) -> MySnapshot<'a> {
self.ldb_rwlock.read();
let snapshot = self.ldb.snapshot();
MySnapshot { snapshot }
}
}
This would let me safely get a snapshot as it would block until any write locks are released, and the end user has no way of mutating ldb except through the APIs I implement so as long as I always use the ldb_rwlock before attempting to read/write to ldb it should be safe. However, I feel like this would invalidate the way rust intended RwLock to be used and doesn't feel very idiomatic. Is there any way to resolve this?
Also the reason why I don't wrap ldb in an Arc is because the Cache struct will be wrapped in an Arc and shared across threads, and I only want a lock on the ldb field so that only read/writes to the database requires a lock. I'm still quite new to rust and concurrency in general though, so I'm not sure if what I'm doing is the best way to solve this.