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>>
}
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.
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());
}
snowbane
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") })
// ]}
// ]}])
// }
}
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") })
// ]}
// ]}])
// }
}
来源:https://stackoverflow.com/questions/50021897/how-to-implement-serdeserialize-for-a-boxed-trait-object