Is there any way to deserialize a Box<dyn Trait>?

I have the following trait and type:

trait Previous {}

trait ToSerde {}

#[derive(Clone, Serialize, Deserialize)]
struct MyStruct<T>(T);

impl<T: Previous + Serialize> ToSerde for MyStruct<T> {}

impl Previous for i32 {}

And If I need to serialize the following my_struct_box: Box<dyn ToSerde> :

let my_struct = MyStruct(1i32);
let my_struct_box: Box<dyn ToSerde> = my_struct_box(my_struct);

I can use the package typetag like this:

trait Previous {}

#[typetag::serialize(tag = "to_serde")]
trait ToSerde {}

#[derive(Clone, Serialize, Deserialize)]
struct MyStruct<T>(T);

#[typetag::serialize]
impl<T: Previous + Serialize> ToSerde for MyStruct<T> {}

impl Previous for i32 {}

But the package can not Deserialize my_struct_box.
So, put others like safety, performace aside, is there any way to deserialize the object serialized from my_struct_box?

Replace typetag::serialize with typetag::serde so you get the deserialize impls

1 Like

I tried this:

trait Previous {}

#[typetag::serde(tag = "to_serde")]
trait ToSerde {}

#[derive(Clone, Serialize, Deserialize)]
struct MyStruct<T>(T);

#[typetag::serde]
impl<T: Previous + Serialize> ToSerde for MyStruct<T> {}

impl Previous for i32 {}

But it has compile error:

error: deserialization of generic impls is not supported yet; use #[typetag::serialize] to generate serialization only
   --> src\ofac.rs:560:5
    |
560 | impl<T: Previous + Serialize> ToSerde for MyStruct<T> {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^

The following is the broader context I am facing:

trait Previous {
    fn get_i32(&self) -> i32;
}
#[typetag::serialize(tag = "to_serde")]
trait ToSerde {
    fn calc(&self) -> i32;
}

#[derive(Clone, Serialize, Deserialize)]
struct MyStruct<T>(T);

#[typetag::serialize]
impl<T: Previous + Serialize> ToSerde for MyStruct<T> {
    fn calc(&self) -> i32 {
        self.0.get_i32() + 1
    }
}

#[derive(Clone, Serialize, Deserialize)]
struct MyType1;
#[derive(Clone, Serialize, Deserialize)]
struct MyType2;
#[derive(Clone, Serialize, Deserialize)]
struct MyType3;

impl Previous for MyType1 {
    fn get_i32(&self) -> i32 { 1 }
}
impl Previous for MyType2 {
    fn get_i32(&self) -> i32 { 2 }
}
impl Previous for MyType3 {
    fn get_i32(&self) -> i32 { 3 }
}

struct SaveAsFile {
    box_vec: Vec<Box<dyn ToSerde>>
}

impl SaveAsFile {
    fn final_calc(&self) -> Vec<i32> {
        self.box_vec.iter()
                    .map(|x| x.calc())
                    .collect()
    }
}

It takes long time to get a instance of SaveAsFile, which I need to save it on a local file. There is many MyType to be a field of MyStruct, so I seems that generic implementing like impl<T: Previous + Serialize> ToSerde for MyStruct<T> is unavoidable.

Oh my bad, I hadn't run into that restriction before.

I'm not sure there's a great way to make that work. There's an open issue in the repo but it doesn't look like there's consensus on how it should be implemented.

I think it should be possible to create a second layer of typetag traits for the inner type and then implement ToSerde for a boxed trait object of that inner trait.

use serde::{Deserialize, Serialize};
use std::fmt::Debug;

#[typetag::serde(tag = "previous")]
trait Previous {
    fn as_debug(&self) -> &dyn Debug;
}

#[typetag::serde(tag = "to_serde")]
trait ToSerde {
    fn previous(&self) -> &dyn Previous;
}

#[derive(Clone, Serialize, Deserialize)]
struct MyStruct<T>(T);

impl<'a, T: Previous + 'a> MyStruct<T> {
    fn box_inner(self) -> MyStruct<Box<dyn Previous + 'a>> {
        MyStruct(Box::new(self.0))
    }
}

#[typetag::serde]
impl ToSerde for MyStruct<Box<dyn Previous>> {
    fn previous(&self) -> &dyn Previous {
        &*self.0
    }
}

#[typetag::serde]
impl Previous for i32 {
    fn as_debug(&self) -> &dyn Debug {
        self
    }
}

fn main() {
    let my_struct = MyStruct(1i32);
    let my_struct_box: Box<dyn ToSerde> = Box::new(my_struct.box_inner());

    let string = serde_json::to_string_pretty(&my_struct_box).unwrap();
    println!("{string}");
    let de: Box<dyn ToSerde> = serde_json::from_str(&string).unwrap();

    println!("Deserialized value: {:?}", de.previous().as_debug());
}

Output:

{
  "to_serde": "MyStruct",
  "previous": "i32",
  "value": 1
}
Deserialized value: 1

Kind of awkward but it does work

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.