serializationrustserdecbor

How do I deserialize into trait, not a concrete type?


I'm trying to do struct serialization, in which the bytes would eventually be sent down a pipe, reconstructed and methods be called on them.

I created a trait these structs would implement as appropriate and I'm using serde and serde-cbor for serialization:

extern crate serde_cbor;
#[macro_use]
extern crate serde_derive;
extern crate serde;

use serde_cbor::ser::*;
use serde_cbor::de::*;

trait Contract {
    fn do_something(&self);
}

#[derive(Debug, Serialize, Deserialize)]
struct Foo {
    x: u32,
    y: u32,
}

#[derive(Debug, Serialize, Deserialize)]
struct Bar {
    data: Vec<Foo>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Baz {
    data: Vec<Foo>,
    tag: String,
}

impl Contract for Bar {
    fn do_something(&self) {
        println!("I'm a Bar and this is my data {:?}", self.data);
    }
}

impl Contract for Baz {
    fn do_something(&self) {
        println!("I'm Baz {} and this is my data {:?}", self.tag, self.data);
    }
}

fn main() {
    let data = Bar { data: vec![Foo { x: 1, y: 2 }, Foo { x: 3, y: 4 }, Foo { x: 7, y: 8 }] };
    data.do_something();

    let value = to_vec(&data).unwrap();
    let res: Result<Contract, _> = from_reader(&value[..]);
    let res = res.unwrap();
    println!("{:?}", res);
    res.do_something();
}

When I try to reconstruct the bytes using the trait as the type (given that I wouldn't know which underlying object is being sent), the compiler complains that the trait does not implement the Sized trait:

error[E0277]: the trait bound `Contract: std::marker::Sized` is not satisfied
  --> src/main.rs:52:15
   |
52 |     let res: Result<Contract, _> = from_reader(&value[..]);
   |              ^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `Contract`
   |
   = note: `Contract` does not have a constant size known at compile-time
   = note: required by `std::result::Result`

I guess it makes sense since the compiler doesn't know how big the struct is supposed to be and doesn't know how to line up the bytes for it. If I change the line where I deserialize the object to specify the actual struct type, it works:

let res: Result<Bar, _> = from_reader(&value[..]);

Is there a better pattern to achieve this serialization + polymorphism behavior?


Solution

  • It looks like you fell into the same trap that I fell into when I moved from C++ to Rust. Trying to use polymorphism to model a fixed set of variants of a type. Rust's enums (similar to Haskell's enums, and equivalent to Ada's variant record types) are different from classical enums in other languages, because the enum variants can have fields of their own.

    I suggest you change your code to

    #[derive(Debug, Serialize, Deserialize)]
    enum Contract {
        Bar { data: Vec<Foo> },
        Baz { data: Vec<Foo>, tag: String },
    }
    
    #[derive(Debug, Serialize, Deserialize)]
    struct Foo {
        x: u32,
        y: u32,
    }
    
    impl Contract {
        fn do_something(&self) {
            match *self {
                Contract::Bar { ref data } => println!("I'm a Bar and this is my data {:?}", data),
                Contract::Baz { ref data, ref tag } => {
                    println!("I'm Baz {} and this is my data {:?}", tag, data)
                }
            }
        }
    }