首页 > 解决方案 > 如何指定特征的所有实现者也必须实现序列化?

问题描述

我很想知道通过内置反射可以节省多少样板。

一点背景

我在结构化日志背后的想法是使用各种小型定制类型将内容与表示分离。而不是非结构化logger.info("Found a bar with {} foos", bar.foo)的使用类似的东西logger.info(FoundBar{ _bar: bar })

我的 Rust 风格

定义特征,提供默认实现:

trait Log {
    fn to_log(&self) -> String {
        serde_json::to_string(&self).unwrap()
    }
}

(RLS 已经画出了愤怒的红色曲线,但请耐心等待)

定义要记录的简单类型:

#[derive(Serialize)]
struct Message {
    msg: String,
}

并让它使用默认实现:

impl Log for Message {}

最后是根据特征定义的多态日志记录函数:

fn log(log: &Log) {
    println!("serialized = {}", log.to_log());
}

编译器抱怨:

error[E0277]: the trait bound `Self: _IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` is not satisfied
 --> src\main.rs:8:9
  |
8 |         serde_json::to_string(&self).unwrap()
  |         ^^^^^^^^^^^^^^^^^^^^^ the trait `_IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` is not implemented for `Self`
  |
  = help: consider adding a `where Self: _IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` bound
  = note: required because of the requirements on the impl of `_IMPL_DESERIALIZE_FOR_Message::_serde::Serialize` for `&Self`
  = note: required by `serde_json::ser::to_string`

where Self建议添加到我的 trait 函数只会产生不同的错误 ( error[E0433]: failed to resolve. Use of undeclared type or module _IMPL_DESERIALIZE_FOR_Message),但除此之外,让 Serde 的这个实现细节泄漏到我的代码中似乎是个坏主意。

我如何可移植地限制我的特征(使用where?)仅适用于具有正确派生的类型?更好的是,我可以使用 trait 将派生功能“注入”到类型中吗?

标签: rustserde

解决方案


如果您在 playground 上创建问题的MCVE,您会得到更准确的错误:

error[E0277]: the trait bound `Self: serde::Serialize` is not satisfied
 --> src/lib.rs:6:9
  |
6 |         serde_json::to_string(&self).unwrap()
  |         ^^^^^^^^^^^^^^^^^^^^^ the trait `serde::Serialize` is not implemented for `Self`
  |
  = help: consider adding a `where Self: serde::Serialize` bound
  = note: required because of the requirements on the impl of `serde::Serialize` for `&Self`
  = note: required by `serde_json::ser::to_string`

按照建议,但使用惯用的 supertrait 语法,回答您的问题:

trait Log: serde::Serialize {
    fn to_log(&self) -> String {
        serde_json::to_string(&self).unwrap()
    }
}

出于对象安全的原因,您需要更改日志功能:

fn log(log: &impl Log) {
    println!("serialized = {}", log.to_log());
}

也可以看看:


推荐阅读