dtolnay / erased-serde

Type-erased Serialize, Serializer and Deserializer traits
Apache License 2.0
709 stars 36 forks source link

Not clear on how to use with #derive macros (still) #13

Closed AlexEne closed 6 years ago

AlexEne commented 6 years ago

Hello,

With the risk of being annoying since this was discussed before, I am currently trying to resolve this exact issue here https://github.com/dtolnay/erased-serde/issues/5, however I am stuck at the following step:

error[E0277]: the trait bound `S: erased_serde::Serializer` is not satisfied
   --> src\task.rs:102:31
    |
102 |         self.erased_serialize(&mut serializer)
    |                               ^^^^^^^^^^^^^^^ the trait `erased_serde::Serializer` is not implemented for `S`
    |
    = help: consider adding a `where S: erased_serde::Serializer` bound
    = note: required for the cast to the object type `erased_serde::Serializer`

This comes from something similar to the example on the above-mentioned issue:

// Task  is just a random trait I made.
impl serde::Serialize for Task {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: serde::Serializer
    {
        self.erased_serialize(&mut serializer)
    }
}

Following the suggestion in the error and requiring where S: serde::Serializer + serde::Deserializer doesn't work either. What I am basically trying to do is save a vector of Box using the following:

#[derive(Default, Serialize, Deserialize)]
pub struct WorkbenchComponent {
    tasks: Vec<Box<Task>>,
    ...
}

All Tasks structs that implement the Task trait, also implement Serialize in a similar way to this:

impl Serialize for CraftingTask {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut state = serializer.serialize_struct("CraftingTask", 1)?;
        state.serialize_field("id", &self.id)?;
        state.end()
    }
}

I also tried #derive[Serialize] on the CraftingTask but it doesn't help either. There is also no clear guide on converting the error types to Serializer::Ok / Error. The map Example in the issue I linked above is not valid anymore (The return type is not Result<(), Error>, but Result<S::Ok, S::Error>

error[E0308]: mismatched types
   --> src\task.rs:102:9
    |
99  |   fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    |                                            ----------------------- expected `std::result::Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>` because of return type
...
102 |         self.erased_serialize(&mut serializer)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found struct `erased_serde::ser::Ok`
    |
    = note: expected type `std::result::Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>`
               found type `std::result::Result<erased_serde::ser::Ok, erased_serde::Error>`

If I manage to solve this, I am happy to contribute a guide in the readme for this use case, but I don't really get how I should fix it right now.

One approach would be just manually implementing Workbench serialize (for the holder of Vec, then manually calling some simpler task_serialize function there. Something like this:

impl serde::Serialize for WorkbenchComponent {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: serde::Serializer
    {
        for task in self.tasks.iter() {
            task.special_serialize(&mut serializer);
        }
        [...]
    }
}

In that method I can implement whatever I need for the serialization routine possibly.

AlexEne commented 6 years ago

My cargo.toml:

[dependencies]
sdl2 = "0.31.0"
imgui = "0.0.18"
gl = "0.6.0"
memoffset = "0.1"
png = "0.11.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
erased-serde = "0.3"
dtolnay commented 6 years ago

It is hard to tell without seeing more of your code but serialize_trait_object! is typically how you want to set up serializable trait objects.

trait Task: erased_serde::Serialize {
    /* ... */
}

serialize_trait_object!(Task);
AlexEne commented 6 years ago

Wow, thanks! How did I miss that? It solved the serialize error I was describing.

But now, I have another question for the same scenario. How would deserialize work in that case? I see no macro for deserialize (or maybe I don't know where to look). Do I need to just call erased_serde::deserialize() like so Vec<Box<Task>> tasks = erased_serde::deserialize() for the holder of that array?

P.S After I solve deserialization I will submit a PR with this exact but simplified example because it may be useful for others

dtolnay commented 6 years ago

There is no such thing as Box<Deserialize>. You may be interested in deserializing to serde_value::Value instead when the concrete type is not known.

AlexEne commented 6 years ago

I suspected that's the solution. Thanks for the help.