我可以在Rust中将字符串转换为枚举而不使用宏吗?

时间:2016-08-22 01:18:19

标签: string parsing enums macros rust

例如,如果我的代码如下:

enum Foo {
    Bar,
    Baz,
    Bat,
    Quux
}

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

这显然会失败,因为input不是Foo的方法。我可以手动输入:

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

但我没有获得自动便利。

看起来Java在枚举上有string lookup function用于此特定目的。

是否可以在不编写我自己的宏或从箱子中导入宏的情况下获得这个?

3 个答案:

答案 0 :(得分:12)

您可以使用包enum_derivecustom_derive来做您想做的事。

这是一个例子:

#[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);
}

自定义derive的{​​{1}}允许您使用EnumFromStr方法获取parse

答案 1 :(得分:11)

您应该实现std::str::FromStr特质。

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

代码生成(又称自动便利)和反射通常要付出代价。在实践中,最终可能不会出现多个枚举变体。

运行playground

答案 2 :(得分:3)

与其他答案相同的免责声明:“没有宏”是不可能的。

扩展投票最高的答案。如this thread中所述,custom_derive + enum_derive的组合有些过时了。现代Rust不再需要基于custom_derive的解决方案。

现代的替代方法是strum。用法如下所示:

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()
}

注意:strum中既需要strum_macros也需要Cargo.toml

strum在字符串表示方面也提供了一些很好的灵活性。来自docs

请注意,FromStr的实现默认情况下仅与变体名称匹配。通过#[strum(serialize_all = "snake_case")] type属性,可以选择不同的大小写转换。