I am trying to use serde to serialize trait objects. I had a look at
- How to implement `serde::Serialize` for a boxed trait object?
- How can we write a generic function for checking Serde serialization and deserialization?
- https://github.com/dtolnay/erased-serde
but I do not understand how to put the pieces together. Please show me how to proceed with this.
This code mimics the example in The Rust Programming Language:
use serde::Serialize; // 1.0.126
trait Paint: Serialize {
fn paint(&self) -> String;
}
#[derive(Serialize)]
struct Pen {
color: String,
}
#[derive(Serialize)]
struct Brush {
color: String,
}
impl Paint for Pen {
fn paint(&self) -> String {
return format!("Pen painting with color {}", self.color);
}
}
impl Paint for Brush {
fn paint(&self) -> String {
return format!("Brush painting with color {}", self.color);
}
}
#[derive(Serialize)]
struct Canvas {
height: f32,
width: f32,
tools: Vec<Box<dyn Paint>>,
}
impl Paint for Canvas {
fn paint(&self) -> String {
let mut s = String::new();
for tool in &self.tools {
s.push_str("\n");
s.push_str(&tool.paint());
}
return s;
}
}
fn main() {
let pen = Pen {
color: "red".to_owned(),
};
let brush = Brush {
color: "blue".to_owned(),
};
let canvas = Canvas {
height: 12.0,
width: 10.0,
tools: vec![Box::new(pen), Box::new(brush)],
};
println!("{}", canvas.paint());
serde_json::to_string(&canvas).unwrap();
}
The code does not compile due to the object-safety rules:
error[E0038]: the trait `Paint` cannot be made into an object
--> src/main.rs:33:12
|
33 | tools: Vec<Box<dyn Paint>>,
| ^^^^^^^^^^^^^^^^^^^ `Paint` cannot be made into an object
|
= help: consider moving `serialize` to another trait
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.126/src/ser/mod.rs:247:8
|
247 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
| ^^^^^^^^^ ...because method `serialize` has generic type parameters
|
::: src/main.rs:3:7
|
3 | trait Paint: Serialize {
| ----- this trait cannot be made into an object...
I understand that trait objects cannot have methods with generic parameters, but in my case I only need to serialize the Canvas
struct to JSON. Is there something I could do to make that work?
The ideal serialized output would be
{
"Canvas": {
"height": 12.0,
"width": 10.0,
"tools": {
"Pen": {
"color": "red"
},
"Brush": {
"color": "blue"
}
}
}
}