请考虑以下代码:
#[derive(Debug)]
struct Test {
int: u8,
bits: u8,
hex: u8
}
fn main() {
let t = Test {
int: 1,
bits: 2,
hex: 3
};
println!("{:#?}", t);
}
运行时,输出以下内容:
Test {
int: 1,
bits: 2,
hex: 3
}
我们可以很轻松地转储结构,这很酷,但是我的一些数据结构包含位掩码或其他不容易在基数10中读取的数据。对于位字段,读取它们会更容易输出如0b00000010
。
是否有一种简单的方法可以获得这样的输出,而不直接在结构上实现Debug
特征(另一种类型的Debug
特征会没问题?)
Test {
int: 1,
bits: 0b00000010,
hex: 0x03
}
如果必须的话,我可以使用不同的类型,但是我希望将结构本身的调试特性保持为#[derive(Debug)]
这么简单。
答案 0 :(得分:4)
是否有一种简单的方法可以获得这样的输出,而不直接在结构上实现
Debug
特征(另一种类型的Debug
特征就可以了)
是。 #[derive(Debug)]
只需依次为每个成员调用Debug
即可实现Debug::debug
。只需按照How to implement a custom 'fmt::Debug' trait?中的说明操作即可获得新类型包装。
use std::fmt;
#[derive(Debug)]
struct Test {
int: u8,
bits: Bits,
hex: u8
}
struct Bits(u8);
impl fmt::Debug for Bits {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0b{:08b}", self.0)
}
}
在另一个方向上,在主结构上有一个方法返回另一个“用于显示目的”的结构并不奇怪,类似于std::path::Display
。这允许您将复杂的显示逻辑移动到单独的类型,同时允许您的原始结构不具有可能妨碍您的新类型:
use std::fmt;
impl Test {
fn pretty_debug(&self) -> PrettyDebug {
PrettyDebug {
int: &self.int,
bits: Bits(&self.bits),
hex: &self.hex,
}
}
}
#[derive(Debug)]
struct PrettyDebug<'a> {
int: &'a u8,
bits: Bits<'a>,
hex: &'a u8
}
struct Bits<'a>(&'a u8);
impl<'a> fmt::Debug for Bits<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0b{:08b}", self.0)
}
}
引用u8
有点愚蠢,但引用是这里最通用的解决方案 - 为您的案例选择合适的数据类型。
您还可以直接为Debug
类型实施PrettyDebug
:
struct PrettyDebug<'a>(&'a Test);
impl<'a> fmt::Debug for PrettyDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Test")
.field("int", &self.0.int)
.field("bits", &format_args!("0b{:08b}", self.0.bits))
.field("hex", &format_args!("0x{:02x}", self.0.hex))
.finish()
}
}