Rust 如何指定trait的所有实现者也必须实现Serialize?

Rust 如何指定trait的所有实现者也必须实现Serialize?,rust,serde,Rust,Serde,我很想知道通过内置反射可以节省多少样板文件 一点背景 我在结构化日志背后的想法是使用各种小型定制类型来分离内容和表示。除了非结构化的logger.info(“找到一个带有{}foos的条”,bar.foo)之外,还可以使用类似logger.info(FoundBar{u bar:bar}) 我铁锈般的做法 定义一个日志特征 提供一个默认实现,该实现调用Serde机制来序列化类型(在本例中为JSON) 通过让日志类型“继承”默认实现,可以轻松定义日志类型 利润 定义特征,提供默认impl: t

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

一点背景 我在结构化日志背后的想法是使用各种小型定制类型来分离内容和表示。除了非结构化的
logger.info(“找到一个带有{}foos的条”,bar.foo)
之外,还可以使用类似
logger.info(FoundBar{u bar:bar})

我铁锈般的做法
  • 定义一个
    日志
    特征
  • 提供一个默认实现,该实现调用Serde机制来序列化类型(在本例中为JSON)
  • 通过让日志类型“继承”默认实现,可以轻松定义日志类型
  • 利润
定义特征,提供默认impl:

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]:不满足特性绑定的'Self:'u IMPL\u反序列化'u FOR_Message::\u serde::Serialize'
-->src\main.rs:8:9
|
8 | serde_json::to_string(&self).unwrap()
|^^^^^^^^^^^^^^^^^^^^^^^^^^^未为“Self”实现特性“\u IMPL\u DESERIALIZE\u FOR\u Message::\u serde::Serialize”`
|
=帮助:考虑添加一个````:y:IpMyReulialIZeE.FuxMe:::SySerd::序列化“绑定”
=注意:由于“\u impl\u反序列化”FOR\u消息的impl要求::\u serde::Serialize`FOR`&Self,因此需要此项`
=注意:`serde_json::ser::to_字符串所需`
where Self
建议添加到my trait函数中只会产生不同的错误(
error[E0433]:无法解决。使用未声明的类型或模块_IMPL_DESERIALIZE_FOR_Message
),但除此之外,将Serde的此实现细节泄漏到我的代码中似乎是个坏主意(TM)

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

如果创建of,则会得到更准确的错误:

error[E0277]:特性绑定'Self:serde::Serialize'不满足
-->src/lib.rs:6:9
|
6 | serde_json::to_string(&self).unwrap()
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^未为` Self'实现`
|
=帮助:考虑添加“自已:Serd::序列化”绑定
=注意:由于对`serde::Serialize` for`&Self的impl的要求,因此需要`
=注意:`serde_json::ser::to_字符串所需`
按照建议,但使用惯用的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());
}
另见:


使用trait继承是可行的,但是使用正确的Serde trait,而不是编译器建议的:

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

为什么操场错误信息更清晰?更新(不稳定?)版本的Rust/compiler?我使用的是stable-x86_64-pc-windows-msvc,它使用的是rustc 1.30。1@dlw我想这是最小的方面。游乐场也在使用1.30.1(目前),但本例中仅使用了两个板条箱。您能否详细说明“物体安全原因”?为什么我要从未命名对象借用而不是将所有权传递给
log()
?[code>logger.info(FoundBar{u bar:bar})]等调用产生的未命名对象@dlw您可以展开吗?这就是所有4个链接问答的内容;我不想进一步复制现有信息。我为什么要借钱?这取决于你。您的原始代码采用了引用特征对象(
&Log
),因此切换到泛型类型的引用是最小的更改。您也可以选择
impl Log
@Shepmaster好的评论:事后看来,问题应该是“[…]实现serde::Serialize”,这已经是一半的答案了……我希望这就是您的意思。我不认为有一个更准确的标题来吸引未来的搜索者有什么害处。