Rust 中带有 RwLockGuard 的 HashMap 记录的游标

Aym*_*our 6 rust rwlock

我是 Rust 的新手,我正在尝试实现一个简单的、线程安全的内存键值存储,HashMapRwLock. 我的代码如下所示:

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)

有任何想法吗?

Sve*_*ach 6

正如评论中提到的,问题是结构在 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)

这确保锁的寿命足够长以完成扫描。

操场上的完整代码