2017-02-22 4 views
8

J'essaye de faire la sérialisation de structure, dans laquelle les octets seraient finalement envoyés vers le bas d'un tuyau, reconstruit et les méthodes soient appelées sur eux.Comment désérialiser en trait, pas un type concret?

J'ai créé un trait ces struct mettraient en œuvre selon le cas et j'utilise serde et serde-DDDC pour la sérialisation:

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(); 
} 

Lorsque je tente de reconstituer les octets en utilisant le trait comme le type (étant donné que Je ne sais pas quel objet sous-jacent est envoyé), le compilateur se plaint que le trait ne met pas en œuvre le trait Sized:

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` 

Je suppose que cela a du sens puisque le compilateur ne sait pas quelle est la taille de la structure et ne sait pas comment aligner les octets pour cela. Si je change la ligne où je désérialiser l'objet de préciser le type de struct réelle, cela fonctionne:

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

Y at-il un meilleur modèle pour obtenir ce comportement de sérialisation + polymorphisme?

+2

... Je ne pense pas que vous pouvez le faire. Vous ne pouvez pas récupérer la structure si vous ne connaissez pas son type concret, et vous ne pouvez pas appeler des méthodes à moins que vous n'ayez un pointeur vers son vtable - que vous ne pouvez pas comprendre sauf si vous avez accès à son type concret. Pouvez-vous sérialiser un vtable? – trentcl

+0

Semble être le cas, mais j'espérais que quelqu'un soulignerait quelque chose qui me manque. J'ai une solution non-idiomatique pour cela mais ajoute le couplage au code ... donc je cherche quelque chose de mieux. – Dash83

+3

Etes-vous sûr de vouloir un polymorphisme et pas simplement une énumération? Avez-vous besoin de votre code pour travailler avec les types fournis par l'utilisateur? –

Répondre

6

On dirait que vous êtes tombé dans le même piège que je suis tombé lorsque je suis passé de C++ à Rust. Essayer d'utiliser le polymorphisme pour modéliser un ensemble fixe de variantes d'un type. Les enums de Rust (semblables aux enums de Haskell, et équivalents aux types d'enregistrements variant d'Ada) sont différents des enums classiques dans d'autres langues, car les variantes enum peuvent avoir leurs propres champs.

Je vous suggère de changer votre code à

#[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) 
      } 
     } 
    } 
} 
+0

Utilisé les structures Bar et Baz comme les données associées pour l'énumération, mais est allé à peu près avec ce design autrement. Merci! – Dash83

+0

Qu'en est-il s'il existe un ensemble arbitraire de caractères d'un trait avec des paramètres de type? – Shisoft

+0

@Shisoft pas sûr de comprendre. Pourquoi ne pas ouvrir une nouvelle question? –