the way I define the serializer adapter, you need to manually create json serializers using writers, basically you create your own convenient serialize function similar to serde_json::to_writer()
, to_vec()
, or to_string()
.
but that's not the point: the serializer example is just an example to demonstrate the technique, I'm not saying you should use this exact design.
by adapter traits, what I really mean is, in order to use dynamic dispatch, you can create your own object-safe trait that can be implemented on generic types with the non-object-safe trait bounds, I'm not suggesting you need to make one-to-one corresponding traits for every non-object-safe trait.
take the from_bytes()
function in OP as an example, you don't need to create a DynSerializeJson
and DynParseProtobuf
, and use these trait object in your code. you can just create whatever trait that suits your use case, e.g. a ProtobufToJson
trait can be defined like this:
/// in order for this trait to be object safe, a `&self` parameter is needed
/// you can use a dummy type as a carrier, whose sole purpose is to hold the vtable
/// note I don't mention the non-object-safe traits when defining the trait,
/// instead I use them as bounds on the blanket implementation instead
trait ProtobufToJson {
fn from_bytes(&self, v: &[u8]) -> String;
}
/// the dummy handle type, here I made it invariant, but you can have different design
struct Converter<T>(PhantomData<fn(T) -> T>);
/// this is the same code from OP
impl<T> ProtobufToJson for Converter<T> where T: MessageFull + Serialize {
fn from_bytes(&self, v: &[u8]) -> String {
let t = T::parse_from_bytes(v).unwrap();
let s = serde_json::to_string_pretty(&t).unwrap();
s
}
}
then you can use it like this:
// assume `converters` is a collection of trait objects, e.g. Vec or HashMap etc.
let mut converters: Vec<Box<dyn ProtobufToJson>> = todo!();
// add trait objects into the collection:
converters.push(Box::new(Converter::<MsgA>::new()));
converters.push(Box::new(Converter::<MsgB>::new()));
converters.push(Box::new(Converter::<Msg10000>::new()));
// I don't know your use case, but suppose somehow you picked an implementation using app specific logic at runtime
let json_string = converters.iter().find(app_specific_logic).unwrap().from_bytes(&protobuf_bytes);