Search code examples
rustdeserializationserdeserde-json

Implement Deserialize for a structure with Box


A struct containing Box as member variable:

type Item = dyn Fn() -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
struct Inner {
    pub data: Box<Item>,
}

// a function, like the type: Item
fn parse() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    println!("parse called");
    Ok(())
}

now comes the problem that custom implement a deserialize for struct Inner to de-serialize:

let s = r#"{"data": "parse"}"#;

into struct Inner:

{
    data: Box::new(parse)
}

I know serde don't implement deserialize for Box<T>, and have to implement Deserialize manually, here is my code followed the example given by docmentaion:

use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
use std::fmt;

type Item = dyn Fn() -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
struct Inner {
    pub data: Box<Item>,
}

fn parse() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    println!("parse called");
    Ok(())
}

impl<'de> Deserialize<'de> for Inner {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        enum Field {
            Data,
        };

        impl<'de> Deserialize<'de> for Field {
            fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
            where
                D: Deserializer<'de>,
            {
                struct FieldVisitor;

                impl<'de> Visitor<'de> for FieldVisitor {
                    type Value = Field;

                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                        formatter.write_str("`data` only")
                    }

                    fn visit_str<E>(self, value: &str) -> Result<Field, E>
                    where
                        E: de::Error,
                    {
                        match value {
                            "data" => Ok(Field::Data),
                            _ => Err(de::Error::unknown_field(value, FIELDS)),
                        }
                    }
                }

                deserializer.deserialize_identifier(FieldVisitor)
            }
        }

        struct InnerVisitor;

        impl<'de> Visitor<'de> for InnerVisitor {
            type Value = Inner;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct Inner")
            }

            fn visit_map<V>(self, mut map: V) -> Result<Inner, V::Error>
            where
                V: MapAccess<'de>,
            {
                let mut data = None;
                while let Some(key) = map.next_key()? {
                    match key {
                        Field::Data => {
                            if data.is_some() {
                                return Err(de::Error::duplicate_field("data"));
                            }
                            data = Some(map.next_value()?);
                        }
                    }
                }
                let data = data.ok_or_else(|| de::Error::missing_field("data"))?;
                //
                // do something on the `data` and finally return a Item-like function  (***)
                //
                Ok(Inner {
                    data: Box::new(parse),
                }) // (***)
            }
        }

        const FIELDS: &'static [&'static str] = &["data"];
        deserializer.deserialize_struct("Inner", FIELDS, InnerVisitor)
    }
}

fn main() {
    let s = r#"{"data": "parse"}"#;
    let inner: Inner = serde_json::from_str(s).unwrap();
}

However, when I run these code it get error:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `inner`
  --> src/main.rs:93:9
   |
93 |     let inner: Inner = serde_json::from_str(s).unwrap();
   |         ^^^^^ help: if this is intentional, prefix it with an underscore: `_inner`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `data`
  --> src/main.rs:76:21
   |
76 |                 let data = data.ok_or_else(|| de::Error::missing_field("data"))?;
   |                     ^^^^ help: if this is intentional, prefix it with an underscore: `_data`

warning: 2 warnings emitted

    Finished dev [unoptimized + debuginfo] target(s) in 1.12s
     Running `target/debug/playground`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: string \"parse\", expected unit", line: 1, column: 16)', src/main.rs:93:48
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Roughly, I guess the problem lies in (***) of the block, but got no clue how to tackle it. How to correctly implement Deserialize for my use case?


Solution

  • The warning gives you a hint: you didn't use data, so Rust doesn't care what type is inside the Option. Actually I'm surprise it's compiling. Rust assumes you expect (), because of serdes default type, while you expect a String. Your data is simply not consumed so just do:

    let mut data: Option<&str> = None;
    

    Also, I advice to replace all your Field implementation with:

    #[derive(Deserialize)]
    #[serde(field_identifier, rename_all = "lowercase")]
    enum Field {
        Data,
    }
    

    (This stack overflows for unknown reason on playground but I expect this is an issue with the playground)


    Also, you could just use deserialize_with:

    use serde::{Deserialize, Deserializer};
    
    type Item = dyn Fn() -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
    #[derive(Deserialize)]
    struct Inner {
        #[serde(deserialize_with = "deserialize_data")]
        pub data: Box<Item>,
    }
    
    fn deserialize_data<'de, D>(d: D) -> Result<Box<Item>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let data = <&str>::deserialize(d)?;
        println!("{}", data);
        Ok(Box::new(parse))
    }
    
    fn parse() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        println!("parse called");
        Ok(())
    }
    
    fn main() {
        let s = r#"{"data": "parse"}"#;
        let inner: Inner = serde_json::from_str(s).unwrap();
    }
    

    This is way better because the implementation by serde macro could do a lot better code than a naïve implementation.