Курсор записей HashMap с RwLockGuard в Rust - PullRequest
0 голосов
/ 23 ноября 2018

Я новичок в 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());
    }
}

Пока этот код работает нормально.Проблема в том, что я пытаюсь реализовать функцию 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()));
    }
}

Но это не сработало, так как я получил эту ошибку компиляции:

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

Есть идеи?

1 Ответ

0 голосов
/ 23 ноября 2018

Как уже упоминалось в комментариях, проблема в том, что структуры обычно не могут быть самоссылочными в Rust .Структура 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();

Это гарантирует, что блокировка живет достаточно долго для завершения сканирования.

Полный код надетская площадка

...