10

私は、バイトが最終的にパイプに送られ、再構築され、メソッドが呼び出される構造体のシリアライゼーションを行おうとしています。

これらの構造体が適切に実装する特性を作成し、シリアライゼーションに serde と serde-cbor を使用しています。

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

トレイトをタイプとして使用してバイトを再構築しようとすると (送信されている基になるオブジェクトがわからない場合)、コンパイラはトレイトがトレイトを実装していないと文句を言い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`

コンパイラは構造体の大きさを知らず、構造体のバイトを並べる方法も知らないので、それは理にかなっていると思います。オブジェクトをデシリアライズして実際の構造体型を指定する行を変更すると、次のように機能します。

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

このシリアル化 + ポリモーフィズム動作を達成するためのより良いパターンはありますか?

4

3 に答える 3

9

私が C++ から Rust に移行したときに陥ったのと同じ罠に陥ったようです。ポリモーフィズムを使用して、型のバリアントの固定セットをモデル化しようとしています。Rust の列挙型 (Haskell の列挙型に似ており、Ada のバリアント レコード型と同等) は、他の言語の従来の列挙型とは異なります。列挙型のバリアントは独自のフィールドを持つことができるからです。

コードを次のように変更することをお勧めします

#[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)
            }
        }
    }
}
于 2017-02-24T09:06:26.453 に答える