【问题标题】:How do I get an enum as a string?如何将枚举作为字符串?
【发布时间】:2015-12-19 00:52:42
【问题描述】:

我有一个包含许多值的枚举,我想将其中一个值的名称写入流:

enum Foo {
    Bar = 0x00,
    Baz = 0x01,
    Qux = 0x02,
    // ...
    Quux = 0xFF,
}

我可以推导出Debug 并做

writer.write(format!("I am {:?}", Foo::Quux).as_bytes())

这将输出例如I am Quux。没关系,除了

  • 我想为面向用户的输出执行此操作,因此 Debug 不合适
  • 将枚举作为字符串获取(而不是直接写入流)会非常有帮助,因为这样我就可以将它的长度合并到我想做的一些不稳定的格式计算中。

【问题讨论】:

    标签: rust


    【解决方案1】:

    可能最简单的方法是通过调用Debug 来实现Display

    impl fmt::Display for Foo {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{:?}", self)
            // or, alternatively:
            // fmt::Debug::fmt(self, f)
        }
    }
    

    然后您可以使用to_string() 来获得String 表示:

    let s: String = Foo::Quux.to_string();
    

    如果您有许多要打印的枚举,您可以编写一个简单的宏来为每个枚举生成Display 的上述实现。

    不幸的是,在 Rust 中反射式编程有些困难。例如,没有标准的方法来获取类 C 枚举的所有变体的列表。几乎总是你必须使用自定义编写的宏(或在 crates.io 上找到一些东西)来抽象样板。如果有人写一个 RFC 并且它会被接受,这可能会在未来改变。

    【讨论】:

    • 如果你use std::fmt::{self, Debug, Display},只需调用Debug::fmt(self, f) 更简单。
    • 不幸的是,这似乎在输出周围加上了引号。
    【解决方案2】:

    由于枚举变量的名称是固定的,你不需要分配String&'static str 就足够了。宏可以删除样板:

    macro_rules! enum_str {
        (enum $name:ident {
            $($variant:ident = $val:expr),*,
        }) => {
            enum $name {
                $($variant = $val),*
            }
    
            impl $name {
                fn name(&self) -> &'static str {
                    match self {
                        $($name::$variant => stringify!($variant)),*
                    }
                }
            }
        };
    }
    
    enum_str! {
        enum Foo {
            Bar = 0x00,
            Baz = 0x01,
            Qux = 0x02,
            //...
            Quux = 0xFF,
        }
    }
    
    fn main() {
        assert_eq!(Foo::Baz.name(), "Baz");
    }
    

    更好的是,您可以使用 strum_macros 之类的 crate 导出这些。

    在 strum 0.10 中,您可以使用 AsStaticRef / AsStaticStr 执行完全相同的代码:

    extern crate strum; // 0.10.0
    #[macro_use]
    extern crate strum_macros; // 0.10.0
    
    use strum::AsStaticRef;
    
    #[derive(AsStaticStr)]
    enum Foo {
        Bar = 0x00,
        Baz = 0x01,
        Qux = 0x02,
        //...
        Quux = 0xFF,
    }
    
    fn main() {
        assert_eq!(Foo::Baz.as_static(), "Baz");
    }
    

    在 strum 0.9 中,字符串切片的生命周期是 not 'static in this case

    #[macro_use]
    extern crate strum_macros; // 0.9.0
    
    #[derive(AsRefStr)]
    enum Foo {
        Bar = 0x00,
        Baz = 0x01,
        Qux = 0x02,
        //...
        Quux = 0xFF,
    }
    
    fn main() {
        assert_eq!(Foo::Baz.as_ref(), "Baz");
    }
    

    【讨论】:

    • 这是我找到的最好的一个。现在,可以进行另一个更正以使宏同时支持Bar = 0x00Bar 枚举类型。一个有默认值,另一个没有它。您可以更改$($variant:ident = $val:expr),*, --> $($variant:ident = $($val:expr)?),*,$($variant = $val),* --> $($variant:ident = $val:expr),*,。现在完美了!
    • 也许您甚至可以将const fn 用于fn name()
    • @nalply 是的,这在现代 Rust 版本中应该是可能的。
    • 使用第一个解决方案,如果您想在enum Foo 上使用#[derive(Debug, Clone)] 怎么办? enum_str!{#[derive(Debug, Clone)] enum Foo{Bar}} 导致 no rules expected this token in macro call
    • @ANimator120 您将按照Generating documentation in macros 中概述的步骤进行操作,该步骤演示了如何处理来自derivemeta
    猜你喜欢
    • 1970-01-01
    • 2017-03-22
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多