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<dyn 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<dyn WidgetTrait>
like so:
#[derive(Serialize)]
struct Section {
header: String,
widgets: Vec<Box<dyn 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<dyn WidgetTrait>`
= note: required because of the requirements on the impl of `serde::Serialize` for `std::vec::Vec<std::boxed::Box<dyn 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<dyn 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());
}
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<dyn 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.
as_any()
implementations for your structs that implement WidgetTrait
as per How to get a reference to a concrete type from a trait object?.Serialize
of type Box<dyn WidgetTrait>
Box<Widget>
to the struct so we know the type using as_any()
and downcast_ref()
#[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<dyn 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<dyn 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With