How to Serialize/Deserialize an async_std::sync::RwLock<T> where T: Serialize + Deserialize

I want to be able to serialize a Arc<RwLock<T>> where T: Serialize + Deserialize entity. I have the features=['rc', 'derive'] for Serde too, but it does not cover locking mechanisms that are async. While most entities can automatically have a trait applied via the #derive[Serialize, Deserialize] macro, this is not possible with the async version of RwLock. For now, I'm having to serialize/deserialize from the inner T, but this is not desirable in my current setup.

I know that custom impl's for Serialize and Deserialize are possible, but I'm confused about writing one for some struct such as:

struct MyStruct<T> where T: Serialize + Deserialize {
    inner: Arc<RwLock<T>>
}

impl<T> Serialize for MyStruct<T> ...

impl<T> Deserialize for MyStruct<T> ...

Consider looking into serde field attributes. You can tell serde to use a custom function for that field. Since you want both serialization and deserialization, you probably want the with attribute, which looks something like this

#[derive(Serialize, Deserialize)]
struct MyStruct<T> where T: Serialize + DeserializeOwned {
    #[serde(with = "arc_rwlock_serde")]
    inner: Arc<RwLock<T>>
}

mod arc_rwlock_serde {
    use serde::{Deserialize, Serialize};
    use serde::de::Deserializer;
    use serde::ser::Serializer;
    use std::sync::{Arc, RwLock};

    pub fn serialize<S, T>(val: &Arc<RwLock<T>>, s: S) -> Result<S::Ok, S::Error>
        where S: Serializer,
              T: Serialize,
    {
        T::serialize(&*val.read().unwrap(), s)
    }

    pub fn deserialize<'de, D, T>(d: D) -> Result<Arc<RwLock<T>>, D::Error>
        where D: Deserializer<'de>,
              T: Deserialize<'de>,
    {
        Ok(Arc::new(RwLock::new(T::deserialize(d)?)))
    }
}

playground

2 Likes

Oh wait, let me guess: The async version of RwLock wouldn't work in my snippet, because read is an async function? Sorry, there's a reason it doesn't implement Serialize, and that's because the Serialize impl would have to somehow await this lock, which it can't from synchronous code.

I suppose you are correct. I think the "dirty" workaround would be to block_on?

No, block_on() within async code cannot be a viable solution. You should .read().await to get the inner value and construct new struct without lock, and serialize it instead.

Yeah, .awaiting the future was sort of implied by the block_on context.

Again, block_on() within async code cannot be a viable solution. It's nothing better than locking synchronous mutex within async context. It can be ok it you've not setup the async runtime and all other code is synchronous. But why use async RwLock here?

1 Like

Ah, that's right. We can't spawn an event loop inside an event loop. No inception! :frowning:

Perhaps I'll just need to use into_inner and ensure in my program's logic that other pointers to the object aren't being engaged

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.