How to initialize and get reference data from Arc<tokio::sync::RwLock>

I'm trying to create a database that fetches data asynchronously from http when there is no data, and retrieves a reference to that data.

I wrote the following code, but I get an error that I can't take temporary values outside the function.

use std::collections::HashMap;

type Data = std::sync::Arc<tokio::sync::RwLock<HashMap<String, String>>>;

async fn get_data<'a>(data: &'a Data, key: &str) -> &'a str {
    let mut data = data.write().await;
    if let Some(d) = data.get(key) {
        return d;
    }

    // Actually, get data by http request or something like that
    data.insert(key.to_string(), "new_data".to_string());

    data.get(key).unwrap()
}

Yes, tokio::RwLock has a read lock and cannot take the value out of scope.

I searched for some useful sites, but I couldn't find a solution.

Does anyone know how to define a function that can retrieve a reference when a key is specified and initialize the data asynchronously as needed?

Sounds like you're looking for some kind of arena instead of HashMap, so that you can insert a new value into it without invalidating existing references. Is that right, or I'm looking in a wrong direction?

I didn't think about using Arena.

What I was looking for was something similar to HashMap::entry for async processing.

Am I overlooking something?

In concrete case, I am writing code to retrieve the target schema from the JSON Schema Store, and lazily read the partial JSON Schema as needed.

I may have been looking for an arena.

I will continue to investigate and consider implementation!

Thanks

(Wait until after the experiment to see if this is the solution.
If I know it is the answer, click the solve button.)

1 Like

After experimentation, I implemented arena and were able to solve the problem.

I wrote the following code

#[derive(Debug, Clone)]
pub struct Arena<K, V> {
    data: Vec<V>,
    key_map: Arc<tokio::sync::RwLock<HashMap<K, NonZeroUsize>>>,
}

impl<K, V> Arena<K, V>
where
    K: std::hash::Hash + Eq,
{
    pub fn new() -> Self {
        Arena {
            data: Vec::new(),
            key_map: Arc::new(tokio::sync::RwLock::new(HashMap::new())),
        }
    }

    pub async fn insert(&self, key: K, d: V) -> &V {
        let mut key_map = self.key_map.write().await;

        unsafe {
            let mutable_self = self as *const Self as *mut Self;
            (*mutable_self).data.push(d);
        }

        key_map.insert(key, NonZeroUsize::new(self.data.len()).unwrap());

        &self.data[self.data.len() - 1]
    }

    pub async fn get(&self, key: &K) -> Option<&V> {
        self.key_map
            .read()
            .await
            .get(key)
            .map(|index| &self.data[index.get() - 1])
    }

    pub async fn contains_key(&self, key: &K) -> bool {
        self.key_map.read().await.contains_key(key)
    }

    pub async fn update(&self, key: &K, d: V) -> &V {
        let key_map = self.key_map.write().await;
        if let Some(&index) = key_map.get(key) {
            unsafe {
                let mutable_self = self as *const Self as *mut Self;
                (*mutable_self).data[index.get() - 1] = d;
            }

            &self.data[index.get() - 1]
        } else {
            unreachable!("key not found");
        }
    }
}

Thanks!