问题
I ran into a problem trying to create a generic vector for a struct. This was my first attempt:
#[derive(Serialize)]
struct Card {
sections: Vec<Section<WidgetTrait>>
}
#[derive(Serialize)]
struct Section<T: WidgetTrait> {
header: String,
widgets: Vec<T>
}
This has brought me to an error that Sized
is not implemented and WidgetTrait
size is not known at compile time.
My next attempt was to use Box<WidgetTrait>
like so:
#[derive(Serialize)]
struct Section {
header: String,
widgets: Vec<Box<WidgetTrait>>
}
Playground
This has led me to an error:
error[E0277]: the trait bound `WidgetTrait: serde::Serialize` is not satisfied
--> src/main.rs:11:10
|
11 | #[derive(Serialize)]
| ^^^^^^^^^ the trait `serde::Serialize` is not implemented for `WidgetTrait`
|
= note: required because of the requirements on the impl of `serde::Serialize` for `std::boxed::Box<WidgetTrait>`
= note: required because of the requirements on the impl of `serde::Serialize` for `std::vec::Vec<std::boxed::Box<WidgetTrait>>`
= note: required by `serde::ser::SerializeStruct::serialize_field`
My goal is for the widgets vector in Section
struct to be able to accept different types of widgets that implement WidgetTrait
trait, just like you would with an interface.
回答1:
For serializing Serde trait objects you should use erased-serde.
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate erased_serde;
extern crate serde;
extern crate serde_json;
#[derive(Serialize)]
struct Card {
sections: Vec<Section>,
}
#[derive(Serialize)]
struct Section {
header: String,
widgets: Vec<Box<WidgetTrait>>,
}
#[derive(Serialize)]
struct Image {
image_url: String,
}
#[derive(Serialize)]
struct KeyValue {
top_label: String,
content: String,
}
trait WidgetTrait: erased_serde::Serialize {}
impl WidgetTrait for Image {}
impl WidgetTrait for KeyValue {}
serialize_trait_object!(WidgetTrait);
fn main() {
let card = Card {
sections: vec![
Section {
header: "text".to_owned(),
widgets: vec![
Box::new(Image {
image_url: "img".to_owned(),
}),
Box::new(KeyValue {
top_label: "text".to_owned(),
content: "text".to_owned(),
}),
],
},
],
};
println!("{}", serde_json::to_string_pretty(&card).unwrap());
}
回答2:
I got around the compiler errors:
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
use serde::ser::{Serialize, Serializer, SerializeStruct};
#[derive(Serialize)]
struct Card {
sections: Vec<Section>
}
#[derive(Serialize)]
struct Section {
header: String,
widgets: Vec<Box<WidgetTrait>>
}
#[derive(Serialize)]
struct Image {
#[serde(rename = "imageUrl")]
image_url: String
}
#[derive(Serialize)]
struct KeyValue {
#[serde(rename = "topLabel")]
top_label: String,
content: String
}
trait WidgetTrait {}
impl Serialize for WidgetTrait {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let s = serializer.serialize_struct("???", 3)?;
s.end()
}
}
impl WidgetTrait for Image {}
impl WidgetTrait for KeyValue {}
fn main() {
// let test = ResponseMessage {
// text: None,
// cards: Some(
// vec![Card { sections: vec![
// Section { header: format!("text"), widgets: vec![
// Box::new(Image { image_url: format!("img") })
// ]},
// Section { header: format!("text"), widgets: vec![
// Box::new(KeyValue { top_label: format!("text"), content: format!("text") }),
// Box::new(KeyValue { top_label: format!("text"), content: format!("text") })
// ]}
// ]}])
// }
}
Playground
Steps for a working solution.
- Write
as_any()
implementations for your structs that implementWidgetTrait
as per How to get a struct reference from a boxed trait?. - Add implementation for trait
Serialize
of typeBox<WidgetTrait>
- Downcast
Box<Widget>
to the struct so we know the type usingas_any()
anddowncast_ref()
- Use documentation on how to serialize a strongly typed struct
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
use serde::ser::{Serialize, Serializer, SerializeStruct};
use std::any::Any;
#[derive(Serialize)]
struct Card {
sections: Vec<Section>
}
#[derive(Serialize)]
struct Section {
header: String,
widgets: Vec<Box<WidgetTrait>>
}
#[derive(Serialize)]
struct Image {
#[serde(rename = "imageUrl")]
image_url: String
}
#[derive(Serialize)]
struct KeyValue {
#[serde(rename = "topLabel")]
top_label: String,
content: String
}
trait WidgetTrait {
fn as_any(&self) -> &Any;
}
impl Serialize for Box<WidgetTrait> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
return match self.as_any().downcast_ref::<Image>() {
Some(img) => {
let mut widget_serializer = serializer.serialize_struct("Image", 1)?;
widget_serializer.serialize_field("imageUrl", &img.image_url)?;
widget_serializer.end()
},
None => {
let key_value: &KeyValue = match self.as_any().downcast_ref::<KeyValue>() {
Some(k) => k,
None => panic!("Unknown type!")
};
let mut widget_serializer = serializer.serialize_struct("KeyValue", 2)?;
widget_serializer.serialize_field("topLabel", &key_value.top_label)?;
widget_serializer.serialize_field("content", &key_value.content)?;
widget_serializer.end()
}
};
}
}
impl WidgetTrait for Image {
fn as_any(&self) -> &Any {
self
}
}
impl WidgetTrait for KeyValue {
fn as_any(&self) -> &Any {
self
}
}
fn main() {
// let test = ResponseMessage {
// text: None,
// cards: Some(
// vec![Card { sections: vec![
// Section { header: format!("text"), widgets: vec![
// Box::new(Image { image_url: format!("img") })
// ]},
// Section { header: format!("text"), widgets: vec![
// Box::new(KeyValue { top_label: format!("text"), content: format!("text") }),
// Box::new(KeyValue { top_label: format!("text"), content: format!("text") })
// ]}
// ]}])
// }
}
Playground
来源:https://stackoverflow.com/questions/50021897/how-to-implement-serdeserialize-for-a-boxed-trait-object