将枚举作为字符串

时间:2015-09-22 06:48:58

标签: rust

我有一个包含很多值的枚举

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

有时我想将其中一个值的名称写入流中。我可以派生Debug并做

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

将输出例如I am Quux。那很好,除了

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

实现这一目标的最佳途径是什么?

2 个答案:

答案 0 :(得分:29)

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

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并且它会被接受,也许这将在未来发生变化。

答案 1 :(得分:7)

由于枚举变体的名称已修复,因此您无需分配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这样的箱子来推导它们。

在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");
}