How do I get an enum as a string?

Rust

Rust Problem Overview


I have an enum with many values and I'd like to write the name of one of its values to a stream:

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

I can derive Debug and do

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

which will output e.g. I am Quux. That's fine, except that

  • I want to do this for user-facing output, so Debug isn't appropriate
  • It would be very helpful to get the enum as a string (rather than writing directly to a stream), because then I can incorporate its length into some wonky formatting calculations I want to do.

Rust Solutions


Solution 1 - Rust

Probably the easiest way would be to implement Display by calling into 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)
    }
}

Then you can use to_string() to get a String representation:

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

If you have many enums which you want to print, you can write a trivial macro to generate the above implementation of Display for each of them.

Unfortunately, in Rust reflective programming is somewhat difficult. There is no standard way, for example, to get a list of all variants of a C-like enum. Almost always you have to abstract the boilerplate with custom-written macros (or finding something on crates.io). Maybe this will change in future if someone would write an RFC and it would get accepted.

Solution 2 - Rust

Since the names of enum variants are fixed, you don't need to allocate a String, a &'static str will suffice. A macro can remove the boilerplate:

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

Even better, you can derive these with a crate like strum_macros.

In strum 0.10, you can use AsStaticRef / AsStaticStr to do the exact same code:

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

In strum 0.9, the string slice's lifetime is 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");
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIskar JarakView Question on Stackoverflow
Solution 1 - RustVladimir MatveevView Answer on Stackoverflow
Solution 2 - RustShepmasterView Answer on Stackoverflow