如何为一个装箱的 trait 对象实现 `serde::Serialize`?

20

我在试图为一个结构体创建一个通用向量时遇到了问题。这是我的第一次尝试:

#[derive(Serialize)]
struct Card {
    sections: Vec<Section<dyn WidgetTrait>>
}

#[derive(Serialize)]
struct Section<T: WidgetTrait> {
    header: String,
    widgets: Vec<T>
}

这使我遇到了一个错误,即 Sized 未实现,而在编译时无法确定 WidgetTrait 的大小。

我的下一步尝试是使用 Box<dyn WidgetTrait>,如下所示:

#[derive(Serialize)]
struct Section {
    header: String,
    widgets: Vec<Box<dyn WidgetTrait>>
}

沙盒

这导致了一个错误:

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`

我的目标是让Section结构体中的小部件向量能够接受不同类型的小部件,这些小部件实现了WidgetTrait特性,就像你在接口上一样。


5
请问您能提供一个“最小化、完整性、可复现性示例”吗?最好是可以复制粘贴到代码编辑器中并能编译或者给出期望的错误信息。 - Boiethios
Section结构体中使用泛型将不允许您存储实现WidgetTrait的不同小部件,因为Rust在编译期间单态化泛型。只是提供信息,盒式特征通常是一个好方法,或者根据您的用例选择Rc - jonny
你需要为“Section”手动实现“Serialize”。即使每个“WidgetTrait”类型都实现了“Serialize”,也不意味着“WidgeTrait”类型本身实现了它。事实上,没有自动实现的方式。 - Shum
是的,我确实查看了文档,但问题在于我无法找到一种方法来确定来自 Box<WidgetTrait>struct 的类型,以便能够使用强类型的 serializer - snowbane
1
你最新的编辑并没有解决你的问题,它是完全不同的方法。 - Tim Diekmann
显示剩余4条评论
3个回答

15

如果要序列化 Serde trait 对象,您应该使用 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());
}

1
对于那些觉得这不起作用的人,特别注意 serialize_trait_object!(WidgetTrait);。它会有很大的帮助。 - Leonel Sanches da Silva
如果你也想进行反序列化操作,请查看我的答案(https://dev59.com/j1UL5IYBdhLWcg3wopay#76886066)。 - Sridhar Ratnakumar

2

我解决了编译错误:

#[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


工作解决方案的步骤:

  1. 为实现WidgetTrait的您的结构体编写as_any()实现,如如何从trait对象获取具体类型的引用?所述。
  2. 为类型为Box<dyn WidgetTrait>的特性Serialize添加实现
  3. 使用as_any()downcast_ref()Box<Widget>向下转换为结构体,以便我们知道类型
  4. 使用文档中有关如何序列化强类型结构的信息
#[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


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接