【问题标题】:How to specify that all implementers of a trait must also implement Serialize?如何指定特征的所有实现者也必须实现序列化?
【发布时间】:2018-11-26 20:11:11
【问题描述】:

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

一点背景

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

我的 Rust 风格

  • 定义 Log 特征
  • 提供调用 Serde 机制来序列化类型的默认实现(在本例中为 JSON)
  • 通过让它们“继承”默认实现来轻松定义可记录类型
  • 利润

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

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

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

定义要记录的简单类型:

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

并让它使用默认实现:

impl Log for Message {}

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

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 将派生功能“注入”到类型中吗?

【问题讨论】:

  • @Shepmaster 好话:事后看来,问题应该是“[...] 实现 serde::Serialize”,这已经是答案的一半了...
  • 我希望这就是你的意思。我认为拥有更准确的标题以吸引未来的搜索者没有任何害处。

标签: rust serde


【解决方案1】:

如果您创建your problem on the playgroundMCVE,则会得到更准确的错误:

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());
}

另见:

【讨论】:

  • playground 错误信息怎么会更清晰?更新(不稳定?)版本的 Rust/编译器?我在 stable-x86_64-pc-windows-msvc 上,它使用 rustc 1.30.1
  • @dlw 我认为这是 minimal 方面。 Playground 也在使用 1.30.1(目前),但这个例子只使用了两个 crate。
  • 您能否详细说明“对象安全原因”?为什么我想从未命名的对象中借用而不是将所有权传递给log()? [由logger.info(FoundBar{ _bar: bar }) 等调用产生的未命名对象]
  • @dlw 你能继续说一下——这就是所有 4 个链接问答的内容;我不想进一步重复现有信息。 我为什么要借钱——这取决于你。您的原始代码采用了引用特征对象 (&Log),因此切换到对泛型类型的引用是最小的更改。你也可以选择impl Log
【解决方案2】:

使用特征继承有效,但使用正确的 Serde 特征,而不是编译器建议的特征:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多