是否可以一起定义fmt::Display
和fmt::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::Display
和fmt::Debug
的实现?例如,类似impl fmt::Debug, fmt::Display for MyDate { ... }
或类似的东西?
还是把#[derive(Debug)]
放在任何一个类的前面是常见的做法?如果我的问题太天真,我深表歉意,因为我是新手。
在大多数情况下,Display
和Debug
具有不同的输出。如果在两种情况下都使用相同的格式是有意义的,那么您编写的代码就可以了。#[derive(Debug)]
也可以——这是你的决定。我认为需要记住的重要一点是Debug
不应该遗漏任何内容,并且要毫不含糊
在所示的例子中,Display
格式完全准确地显示了结构的所有字段,因此可以重用Display
来实现Debug
。如果您有很多这样的类型,那么可以使用一个简单的宏来生成转发到Display
的Debug
实现。
顺便说一句,我建议使用#[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
才是有趣的。