bright-star
bright-star

Reputation: 6447

Can I convert a string to enum without macros in Rust?

For example, if I have code like:

enum Foo {
    Bar,
    Baz,
    Bat,
    Quux
}

impl Foo {
    from(input: &str) -> Foo {
        Foo::input
    }
}

This will obviously fail because input is not a method of Foo. I can manually type:

from(input: &str) -> Foo {
    match(input) {
        "Bar" => Foo::Bar,
        // and so on...
    }
}

but I'm not getting the automatic convenience.

It looks like Java has a string lookup function on enums for this specific purpose.

Is it possible to get this without writing my own macro or importing one from a crate?

Upvotes: 63

Views: 52229

Answers (3)

bluenote10
bluenote10

Reputation: 26739

Same disclaimer as in other answers: "without macros" isn't possible.

Extending on the highest voted answer. As noted in this thread the combination of custom_derive + enum_derive is somewhat outdated. Modern Rust doesn't need a solution based on custom_derive anymore.

A modern alternative is strum. Usage could look like this:

use strum_macros::EnumString;
use std::str::FromStr;

#[derive(EnumString)]
enum Foo {
    Bar,
    Baz,
    Bat,
    Quux
}

fn example_usage(input: &str) -> Foo {
    Foo::from_str(input).unwrap()
}

Note: You need both strum and strum_macros in your Cargo.toml.

strum also gives some nice flexibility regarding the string representation. From the docs:

Note that the implementation of FromStr by default only matches on the name of the variant. There is an option to match on different case conversions through the #[strum(serialize_all = "snake_case")] type attribute.

Upvotes: 51

Pandemonium
Pandemonium

Reputation: 8390

You should implement std::str::FromStr trait.

use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Foo {
    Bar,
    Baz,
    Bat,
    Quux,
}

impl FromStr for Foo {

    type Err = ();

    fn from_str(input: &str) -> Result<Foo, Self::Err> {
        match input {
            "Bar"  => Ok(Foo::Bar),
            "Baz"  => Ok(Foo::Baz),
            "Bat"  => Ok(Foo::Bat),
            "Quux" => Ok(Foo::Quux),
            _      => Err(()),
        }
    }
}



fn main() {
    // Use it like this
    let f = Foo::from_str("Baz").unwrap();
    assert_eq!(f, Foo::Baz);
}

Code-generation (aka automatic convenience) and reflections usually bear a cost. In practice, it is unlikely that you will end up with more than a few enum variants.

Run in the playground

Upvotes: 100

antoyo
antoyo

Reputation: 11933

Edit: The answer is no. Rust does not provide reflection and usually use #[derive] for that kind of tasks.

You can use the crates enum_derive and custom_derive to do what you want.

Here is an exemple:

#[macro_use]
extern crate custom_derive;
#[macro_use]
extern crate enum_derive;

custom_derive! {
    #[derive(Debug, EnumFromStr)]
    enum Foo {
        Bar,
        Baz,
        Bat,
        Quux
    }
}

fn main() {
    let variable: Foo = "Bar".parse().unwrap();
    println!("{:?}", variable);
}

the derive of the custom EnumFromStr allows you to use the parse method to get a Foo.

Upvotes: 29

Related Questions