一起定义fmt::Display和fmt::Debug



是否可以一起定义fmt::Displayfmt::Debug,即使它们使用相同的实现?

假设我们有以下示例代码(纯粹用于演示目的(:

use std::fmt;
struct MyDate {
pub year: u16,
pub month: u8,
pub day: u8
}
impl PartialEq for MyDate {
fn eq(&self, other: &Self) -> bool {
self.year == other.year && self.month == other.month && self.day == other.day
}
}
impl fmt::Display for MyDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}-{:02}-{:02}", self.year, self.month, self.day)
}
}
impl fmt::Debug for MyDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
fn main() {
let d1 = MyDate { year: 2008, month: 9, day: 10 };
let d2 = MyDate { year: 2008, month: 9, day: 10 };
println!("Date = {}", d1);  // requires fmt::Display
assert_eq!(d1, d2);         // requires fmt::Debug
}

似乎为了使用println打印我的类并使用assert_eq编写单元测试,我需要定义两个fmt特性。有没有一种方法可以使1";打印";同时定义fmt::Displayfmt::Debug的实现?例如,类似impl fmt::Debug, fmt::Display for MyDate { ... }或类似的东西?

还是把#[derive(Debug)]放在任何一个类的前面是常见的做法?如果我的问题太天真,我深表歉意,因为我是新手。

在大多数情况下,DisplayDebug具有不同的输出。如果在两种情况下都使用相同的格式是有意义的,那么您编写的代码就可以了。#[derive(Debug)]也可以——这是你的决定。我认为需要记住的重要一点是Debug不应该遗漏任何内容,并且要毫不含糊

在所示的例子中,Display格式完全准确地显示了结构的所有字段,因此可以重用Display来实现Debug。如果您有很多这样的类型,那么可以使用一个简单的宏来生成转发到DisplayDebug实现。

顺便说一句,我建议使用#[derive(PartialEq)],而不是您展示的手动PartialEq实现。它是完全等效的,避免了忘记比较新添加的字段的危险失败模式。

性能考虑:在Debug实现中,您正在调用同一对象的Display实现:

impl fmt::Debug for MyDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self) // call method from Display
}
}

但是,在执行此操作时,Debug::fmt()会调用write!两次–这不是一个廉价的操作。

一个更好的实现是直接从Display特性调用方法:

impl fmt::Debug for MyDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}

现在write!只被调用一次。

或者只将#[derive(Debug)]放在任何类前面是常见的做法吗?

是的,这很常见。只有当您想要结构中字段的自定义输出时,实现自定义Debug才是有趣的。

最新更新