有没有办法自定义Debug输出而不直接实现Debug特性?

时间:2017-10-14 21:49:49

标签: rust

请考虑以下代码:

#[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)]这么简单。

1 个答案:

答案 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()
    }
}