2017-02-22 81 views
8

我想要做的結構序列化,其中的字節最終將被髮送到一個管道,重建和調用方法。如何反序列化爲特徵,而不是具體類型?

我創建了一個特點,這些結構將實施適當的和我使用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[..]); 

有沒有更好的方式來實現這一系列化+多態行爲?

+2

我......不認爲你能做到這一點。除非你知道它的具體類型,否則你不能恢復這個結構體,除非你有一個指向它的vtable的指針,否則你不能調用它的方法 - 除非你有權訪問它的具體類型,否則你無法弄清楚。你可以序列化一個Vtable嗎? – trentcl

+0

似乎是這樣,但我希望有人會指出我失蹤的東西。我有一個非慣用的解決方案,但增加了耦合到代碼...所以我正在尋找更好的東西。 – Dash83

+3

你確定你想要多態而不僅僅是一個枚舉嗎?你需要你的代碼來處理用戶提供的類型嗎? –

回答

6

它看起來像是陷入了我從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) 
      } 
     } 
    } 
} 
+0

使用結構Bar和Baz作爲枚舉的關聯數據,但在這種設計中使用得非常多。謝謝! – Dash83

+0

如果存在具有類型參數特徵的任意類型集合,那麼該怎麼辦? – Shisoft

+0

@Shisoft不知道我明白。你爲什麼不開一個新的問題? –

相關問題