我正在尝试序列化struct
的集合,这些集合可能是不同类型但实现特征。以下内容未在围栏中编译,但显示了我要做的事情:
extern crate rustc_serialize;
trait Account {
fn get_name(&self) -> &String;
}
#[derive(RustcEncodable, RustcDecodable)]
struct Account1 {
name: String,
}
impl Account1 {
fn new() -> Account1 {
Account1 { name: String::from("Account1") }
}
}
impl Account for Account1 {
fn get_name(&self) -> &String {
&self.name
}
}
#[derive(RustcEncodable, RustcDecodable)]
struct Account2 {
name: String,
}
impl Account2 {
fn new() -> Account2 {
Account2 { name: String::from("Account2") }
}
}
impl Account for Account2 {
fn get_name(&self) -> &String {
&self.name
}
}
#[derive(RustcEncodable, RustcDecodable)]
struct Accounts {
accounts: Vec<Box<Account>>
}
impl Accounts {
fn new() -> Accounts {
let accs: Vec<Box<Account>> = Vec::new();
Accounts { accounts: accs }
}
fn add_account(&mut self, account: Box<Account>) {
self.accounts.push(account);
}
}
fn main() {
let mut accounts = Accounts::new();
let acc1 = Box::new(Account1::new());
accounts.add_account(acc1);
let acc2 = Box::new(Account2::new());
accounts.add_account(acc2);
}
在我的机器上,我收到以下错误:
error: the trait `core::marker::Sized` is not implemented for the type `account::Account` [E0277]
我认为错误是说要使其工作,编译器需要在编译时知道每个Box
-ed元素的大小。我该如何解决这个问题?
我认为我这样做是如何在Java / C ++中实现的,但我应该以不同的方式思考Rust。请建议是否有更好的替代方案来实现这一目标。
答案 0 :(得分:2)
我建议您远离struct
s +普通trait
设置并使用enum
。这显然不允许您的包的用户添加新的帐户类型,但即使以某种方式允许,您的反序列化代码也永远不会知道新类型,也无法反序列化它们。肯定有办法注册&#34;某些反序列化器取决于某些标签,但只要您有一组固定的帐户类型,枚举就允许您使用现有的(反)序列化框架。
首先,您创建一个通用Account
类型,其中包含帐户常用的所有字段
#[derive(RustcEncodable, RustcDecodable)]
struct Account {
name: String,
kind: AccountType,
}
然后你创建一个枚举,其中包含所有accound类型的变体。如果帐户类型需要额外的字段,只需将它们添加到相应的枚举变体。
#[derive(RustcEncodable, RustcDecodable)]
enum AccountType {
Account1,
Account2,
}
您的Accounts
经理甚至不再需要Box
es:
#[derive(RustcEncodable, RustcDecodable)]
struct Accounts {
accounts: Vec<Account>
}
您可以尝试使用Playground中的所有内容,但请确保使用rustc_serialize
而不是我在Playground中使用的黑客,以使其在操场上运行。除前两行之外的所有行都是100%兼容的。
答案 1 :(得分:1)
Encodable
和Decodable
特征的自动生成实现不会做正确的事情;我不确定我以前见过某人尝试序列化特征对象。没有struct
反思知道要为特征输出什么名称和类型。
要解决您的问题,您可以自己实施这些特征。这是一个只创建名称数组的示例:
impl Encodable for Accounts {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_seq(self.accounts.len(), |s| {
for (idx, a) in self.accounts.iter().enumerate() {
try!(s.emit_seq_elt(idx, |s| {
s.emit_str(a.get_name())
}))
}
Ok(())
})
}
}
但是,我不知道你会怎么写一个解码器 - 你会选择哪种具体类型来实现这个特性?
供参考,以下是通过--pretty expanded
rustc
选项找到的自动生成的实现。为了便于阅读,我清理了一下:
impl Decodable for Accounts {
fn decode<D: Decoder>(arg: &mut D) -> Result<Accounts, D::Error> {
arg.read_struct("Accounts", 1, |d| {
let a = try!(d.read_struct_field("accounts", 0usize, Decodable::decode));
Ok(Accounts { accounts: a })
})
}
}
编译错误:
src/main.rs:118:66: 118:83 error: the trait `core::marker::Sized` is not implemented for the type `Account` [E0277]
src/main.rs:118 let a = try!(d.read_struct_field("accounts", 0usize, Decodable::decode));
^~~~~~~~~~~~~~~~~