Rust 中带有 RwLockGuard 的 HashMap 记录游标

Cursor of HashMap records with RwLockGuard in Rust

我是 Rust 的新手,我正在尝试使用 RwLock 中受保护的 HashMap 来实现一个简单的、线程安全的内存键值存储。我的代码如下所示:

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());
    }
}

到目前为止,这段代码工作正常。问题是我正在尝试实现一个 scan() 函数,其中 returns 一个 Cursor 对象,可用于遍历所有记录。我希望 Cursor 对象持有一个 RwLockGuard,它在游标本身被释放之前不会被释放(基本上我不想在 Cursor 存在时允许修改)。

我试过这个:

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()));
    }
}

但这没有用,因为我遇到了这个编译错误:

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 | |     }
   | |_____^

有什么想法吗?

如评论中所述,问题是。您尝试构建的 Cursor 结构包含 MutexGuard 和借用 MutexGuard 的迭代器,这是不可能的(有充分的理由 - 请参阅链接的问题)。

在这种情况下,最简单的解决方法是引入一个单独的结构来存储 MutexGuard,例如

struct StoreLock<'a> {
    guard: RwLockReadGuard<'a, SimpleCollection>,
}

Store上,我们可以引入一个返回StoreLock

的方法
fn lock(&self) -> StoreLock {
    StoreLock { guard: self.0.read().unwrap() }
}

StoreLock 可以公开实际的 scan() 方法(可能还有其他需要持久锁的方法):

impl<'a> StoreLock<'a> {
    fn scan(&self) -> Cursor {
        Cursor { iter: self.guard.iter() }
    }
}

Cursor 结构本身只包含迭代器:

struct Cursor<'a> {
    iter: Iter<'a, String, String>,
}

客户端代码首先需要获取锁,然后获取游标:

let lock = s.lock();
let cursor = lock.scan();

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

Full code on the playground