在Rust中打印所有结构体字段

3

我有大约10个结构体,每个结构体有5-10个字段,我希望能够使用相同的格式将它们打印出来。

我的大多数结构体看起来像这样:

struct Example {
  a: Option<String>,
  b: Option<i64>,
  c: Option<String>,
  ... etc
}

我希望能够定义一个fmt::Displayimpl,而不必再次枚举字段,以避免添加新字段时遗漏任何一个字段。对于这个结构:
let eg = Example{
  a: Some("test".to_string),
  b: Some(123),
  c: None,
}

我想要的输出格式是:


a: test
b: 123
c: -

我目前正在使用#[derive(Debug)],但我不喜欢它打印出Some(X)None和其他一些东西。

如果我知道我的结构体中所有的值都是Option<T: fmt::Display>,那么我是否可以生成一个Display方法而无需再列出字段?


可能是在运行时使用反射枚举结构体字段的重复问题。 - trent
或许也不需要……你没有指定必须在运行时完成。当然,这可以通过处理宏在编译时完成,尽管我自己不知道如何做。 - trent
我更愿意在编译时完成这个任务,但如果必要的话,运行时也可以。我考虑过宏,但不确定如何编写一个能够实现我想要的功能的宏。 - Nick
2个回答

5

这可能不是最简洁的实现方法,但您可以使用 serde crate 来派生出可序列化对象。这里有一个自定义序列化器的例子:https://serde.rs/impl-serializer.html

在您的情况下,可能会更简单(您只需要少量类型,并且可以在任何意外情况下进行 panic/忽略)。

另一种方法是编写宏并创建自己的轻量级序列化解决方案。


谢谢您的建议,这对我以后遇到的另一个问题很有用!我决定采用下面的宏解决方案,这在很大程度上基于:https://dev59.com/DlQJ5IYBdhLWcg3wSj0C#54177889 - Nick

5

我最终通过宏来解决了这个问题。虽然这不是最理想的方法,但已经能够胜任工作。

我的宏目前看起来像这样:

macro_rules! MyDisplay {
    ($struct:ident {$( $field:ident:$type:ty ),*,}) => {
        #[derive(Debug)]
        pub struct $struct { pub $($field: $type),*}

        impl fmt::Display for $struct {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                $(
                    write!(f, "{}: {}\n",
                        stringify!($field).to_string(),
                        match &self.$field {
                            None => "-".to_string(),
                            Some(x) => format!("{:#?}", x)
                        }
                    )?;
                )*
                Ok(())
            }
        }
    };
}

可以像这样使用:

MyDisplay! {
    Example {
        a: Option<String>,
        b: Option<i64>,
        c: Option<String>,
    } 
}

一个带有例子的游乐场: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cc089f8aecaa04ce86f3f9e0307f8785

我的宏基于这里提供给我们的宏 https://dev59.com/DlQJ5IYBdhLWcg3wSj0C#54177889,由Cerberus提供。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接