serde_with icon indicating copy to clipboard operation
serde_with copied to clipboard

Feature Request: Add owned version of SerializeAsWrap

Open Leo1003 opened this issue 8 months ago • 1 comments

SerializeAsWrap is a convenient adapter which provide Serialize implementation over SerializeAs.

However, SerializeAsWrap only store the reference to the data, which makes it not able to return data wrapped with specific SerializeAs impl.

Given an example actix-web api:

#[get("/data")]
pub async fn get_data() -> AppResult<impl Responder> {
    let data = get_data().await?;
    Ok(Json(data))
}

#[get("/data_detailed")]
pub async fn get_data_detailed() -> AppResult<impl Responder> {
    let data = get_data().await?;
    Ok(Json(SerializeAsWrap::<MyData, Detailed>::new(&data)))
}

The above code cannot be compiled since SerializeAsWrap doesn't owns the data.

By adding an owned version of SerializeAsWrap can resolve the above issues:

pub struct SerializeAsWrapOwned<T, U> {
    value: T,
    _phantom: std::marker::PhantomData<U>,
}

impl<T, U> SerializeAsWrapOwned<T, U> {
    pub fn new(value: T) -> Self {
        Self {
            value,
            _phantom: std::marker::PhantomData,
        }
    }

    pub fn into_inner(self) -> T {
        self.value
    }
}

impl<T, U> AsRef<T> for SerializeAsWrapOwned<T, U> {
    fn as_ref(&self) -> &T {
        &self.value
    }
}

impl<T, U> AsMut<T> for SerializeAsWrapOwned<T, U> {
    fn as_mut(&mut self) -> &mut T {
        &mut self.value
    }
}

impl<T, U> serde::Serialize for SerializeAsWrapOwned<T, U>
where
    U: serde_with::SerializeAs<T>,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        U::serialize_as(&self.value, serializer)
    }
}

impl<T, U> From<T> for SerializeAsWrapOwned<T, U> {
    fn from(value: T) -> Self {
        Self::new(value)
    }
}

Leo1003 avatar Mar 18 '25 11:03 Leo1003

This looks reasonable overall. The types were only intended to be used as helpers for implementing SerializeAs/DeserializeAs, but a small wrapper doesn't hurt. It does look similar to DeserializeAsWrap now.

I think it could cover the use case of SerializeAsWrap, but the type annotations might be a bit worse. In that case it might make sense to just have one wrapper and implement all the traits (serialization and deserialization) for it.

jonasbb avatar Mar 18 '25 23:03 jonasbb