我是 Rust 的新手,我正在尝试实现一个简单的、线程安全的内存键值存储,HashMap在RwLock. 我的代码如下所示:
use std::sync::{ Arc, RwLock, RwLockReadGuard };
use std::collections::HashMap;
use std::collections::hash_map::Iter;
type SimpleCollection = HashMap<String, String>;
struct Store(Arc<RwLock<SimpleCollection>>);
impl Store {
fn new() -> Store { return Store(Arc::new(RwLock::new(SimpleCollection::new()))) }
fn get(&self, key: &str) -> Option<String> {
let map = self.0.read().unwrap();
return map.get(&key.to_string()).map(|s| s.clone());
}
fn set(&self, key: &str, value: &str) {
let mut map = self.0.write().unwrap();
map.insert(key.to_string(), value.to_string());
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,这段代码工作正常。问题是我正在尝试实现一个scan()函数,该函数返回一个Cursor可用于迭代所有记录的对象。我希望Cursor对象持有一个RwLockGuard,它在游标本身被释放之前不会被释放(基本上我不想在游标处于活动状态时允许修改)。
我试过这个:
use ...
type SimpleCollection = HashMap<String, String>;
struct Store(Arc<RwLock<SimpleCollection>>);
impl Store {
...
fn scan(&self) -> Cursor {
let guard = self.0.read().unwrap();
let iter = guard.iter();
return Cursor { guard, iter };
}
}
struct Cursor<'l> {
guard: RwLockReadGuard<'l, SimpleCollection>,
iter: Iter<'l, String, String>
}
impl<'l> Cursor<'l> {
fn next(&mut self) -> Option<(String, String)> {
return self.iter.next().map(|r| (r.0.clone(), r.1.clone()));
}
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为我收到了这个编译错误:
error[E0597]: `guard` does not live long enough
--> src/main.rs:24:20
|
24 | let iter = guard.iter();
| ^^^^^ borrowed value does not live long enough
25 | return Cursor { guard, iter };
26 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 22:5...
--> src/main.rs:22:5
|
22 | / fn scan(&self) -> Cursor {
23 | | let guard = self.0.read().unwrap();
24 | | let iter = guard.iter();
25 | | return Cursor { guard, iter };
26 | | }
| |_____^
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
正如评论中提到的,问题是结构在 Rust 中通常不能自引用。在Cursor您要构建结构既包含了MutexGuard和迭代器借用MutexGuard,这是不可能的(有充分的理由-见链接的问题)。
在这种情况下,最简单的解决方法是引入一个单独的结构来存储MutexGuard,例如
struct StoreLock<'a> {
guard: RwLockReadGuard<'a, SimpleCollection>,
}
Run Code Online (Sandbox Code Playgroud)
在 上Store,然后我们可以引入一个方法返回一个StoreLock
fn lock(&self) -> StoreLock {
StoreLock { guard: self.0.read().unwrap() }
}
Run Code Online (Sandbox Code Playgroud)
并且StoreLock可以公开实际scan()方法(可能还有其他需要持久锁的方法):
impl<'a> StoreLock<'a> {
fn scan(&self) -> Cursor {
Cursor { iter: self.guard.iter() }
}
}
Run Code Online (Sandbox Code Playgroud)
该Cursor结构本身只包含迭代器:
struct Cursor<'a> {
iter: Iter<'a, String, String>,
}
Run Code Online (Sandbox Code Playgroud)
客户端代码首先需要获取锁,然后获取游标:
let lock = s.lock();
let cursor = lock.scan();
Run Code Online (Sandbox Code Playgroud)
这确保锁的寿命足够长以完成扫描。