1

I have events with a polymorphic payload property:

use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
pub struct Event<'a> {
    pub topic: String,
    pub key: Option<String>,
    pub payload: Box<dyn EventPayload<'a>>,
}

The only requirement for my payload, is that I want to serialize it using serde_json:

pub trait EventPayload<'a> : Debug + Deserialize<'a> + Serialize {}

The compiler does not allow this pattern:

error[E0038]: the trait `EventPayload` cannot be made into an object
   --> pong/src/event.rs:12:26
    |
12  |         pub payload: Box<dyn EventPayload<'a>>,
    |                          ^^^^^^^^^^^^^^^^^^^^ `EventPayload` cannot be made into an object
    |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>

I know I could use enums for this use case:

enum Event {
  EventA(EventAPayload),
  EventB(EventBPayload),
}

However I don't like this solution because I would have to pin down types of objects that are defined outside of the application (e.g. by a schema registry).

What is the correct pattern to use in rust?

code-gorilla
  • 2,231
  • 1
  • 6
  • 21
  • 1
    The easiest pattern is certainly the enum. And a deserializer for `Event` cannot be derived (though you can maybe implement one manually): serde has no way to know how to construct a `Box`, there is no way to get a list of types that implement a trait. – Caesar Jun 26 '22 at 11:39
  • Look up [`erased_serde`](https://docs.rs/erased-serde/latest/erased_serde/index.html) by the author of serde. While it doesn't allow making `Deserialize` into a trait object (which would match your use case precisely), perhaps you can use its other facilities (such as object-safe `Deserializer`) to implement what you need. – user4815162342 Jun 26 '22 at 19:10

1 Answers1

0

However I don't like this solution because I would have to pin down types of objects that are defined outside of the application (e.g. by a schema registry).

If you don't want to pin down the types, then the compiler won't be able to generate deserialization code for that either. The compiler cannot generate deserialization code for types it doesn't know.

I think the correct solution here would be to implement Deserialize yourself instead of deriving it. It's not actually that hard, don't worry. But the compiler won't be able to figure out how to deserialize an external type, you will have to implement that yourself.

Finomnis
  • 18,094
  • 1
  • 20
  • 27