如何反序列化为特征,而不是具体类型? [英] How do I deserialize into trait, not a concrete type?

查看:37
本文介绍了如何反序列化为特征,而不是具体类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试进行结构序列化,其中字节最终将通过管道发送、重构并在其上调用方法.

我创建了一个特征,这些结构将根据需要实现,我使用 serde 和 serde-cbor 进行序列化:

extern crate serde_cbor;#[宏使用]外部板条箱 serde_derive;外部板条箱 serde;使用 serde_cbor::ser::*;使用 serde_cbor::de::*;特质合约{fn do_something(&self);}#[派生(调试,序列化,反序列化)]结构 Foo {x: u32,y:u32,}#[派生(调试,序列化,反序列化)]结构栏{数据:Vec Foo ,}#[派生(调试,序列化,反序列化)]结构巴兹{数据:Vec Foo ,标签: 字符串,}酒吧的impl合同{fn do_something(&self) {println!("我是一个酒吧,这是我的数据 {:?}", self.data);}}Baz 的 impl 合同 {fn do_something(&self) {println!("我是 Baz {},这是我的数据 {:?}", self.tag, self.data);}}fn 主(){let data = Bar { data: vec![Foo { x: 1, y: 2 }, Foo { x: 3, y: 4 }, Foo { x: 7, y: 8 }] };data.do_something();让值 = to_vec(&data).unwrap();let res: Result= from_reader(&value[..]);让 res = res.unwrap();println!("{:?}", res);res.do_something();}

当我尝试使用 trait 作为类型重建字节时(假设我不知道正在发送哪个底层对象),编译器抱怨 trait 没有实现 Sized特点:

<块引用>

error[E0277]: trait bound `Contract: std::marker::Sized` 不满足-->src/main.rs:52:15|52 |let res: Result= from_reader(&value[..]);|^^^^^^^^^^^^^^^^^^^ 特性 `std::marker::Sized` 没有为 `Contract` 实现|= 注意:`Contract` 在编译时没有已知的常量大小= 注意:`std::result::Result` 需要

我想这是有道理的,因为编译器不知道结构应该有多大,也不知道如何为它排列字节.如果我更改反序列化对象的行以指定实际的结构类型,它会起作用:

let res: Result= from_reader(&value[..]);

有没有更好的模式来实现这种序列化+多态行为?

解决方案

看起来你陷入了我从 C++ 转向 Rust 时陷入的陷阱.尝试使用多态性来建模一组固定的类型变体.Rust 的枚举(类似于 Haskell 的枚举,相当于 Ada 的变体记录类型)与其他语言中的经典枚举不同,因为枚举变体可以有自己的字段.

我建议您将代码更改为

#[derive(Debug, Serialize, Deserialize)]枚举合约{条形{数据:Vec;},Baz { data: Vec, tag: String },}#[派生(调试,序列化,反序列化)]结构 Foo {x: u32,y:u32,}实施合同{fn do_something(&self) {匹配*自我{合同::酒吧{参考数据} =>println!("I'm a Bar and this is my data {:?}", data),Contract::Baz { ref data, ref tag } =>{println!("我是 Baz {},这是我的数据 {:?}", tag, data)}}}}

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?

解决方案

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

这篇关于如何反序列化为特征,而不是具体类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆