36

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?

bright-star
  • 5,602
  • 5
  • 39
  • 80
  • 1
    What if instead of `"Bar"` you need `"Foo::Bar"` or `"my_module::Foo::Bar"` or `"bar"`? That helps explain why there's no pre-made way to do this. There's nothing in the standard library, beyond that you would just be asking for a library / tool recommendation. – Shepmaster Aug 22 '16 at 01:32
  • I don't think your edit on my answer is necessary: you asked for automatic convenience, which you get with the crates I mentioned and you don't have to write you own macro, because the crates already provide the macro. – antoyo Aug 22 '16 at 16:34

3 Answers3

43

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

Pandemonium
  • 6,565
  • 2
  • 29
  • 49
27

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.

antoyo
  • 9,835
  • 3
  • 43
  • 77
  • If it is not what you want, please edit your question, because, as I said [here](http://stackoverflow.com/questions/39070244/can-i-convert-a-string-to-enum-without-macros-in-rust/39070533#comment-65517006), I don't see why it does not answer your question. – antoyo Aug 22 '16 at 21:47
  • 1
    Great answer, but not to this question. I was also looking for an answer. – Flavius May 25 '19 at 19:00
  • I thought the OP explicitly asked "without macros". – vasilakisfil Oct 09 '19 at 07:56
  • I think the answer is that it is not possible without a macro, although I would have this double-checked by a more experienced Rustacean before I try editing the answer to make it clearer. – iago-lito Apr 07 '20 at 10:36
26

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.

bluenote10
  • 20,013
  • 11
  • 98
  • 156