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>>
}
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
.
// [dependencies]
// erased-serde = "0.3"
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
use erased_serde::serialize_trait_object;
use serde::Serialize;
#[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());
}