Closed8

[Rust] Enum と文字列の変換

Takanori IshikawaTakanori Ishikawa

Rust で文字列と Enum の相互変換をやりたい。コマンドラインのオプションを解析するときに、

cmd --arg arg

で渡された arg を Enum に変換したい。しかも、失敗した時には変換候補も表示したい。

Takanori IshikawaTakanori Ishikawa

とりあえず、こんな感じで実装はできるが、毎回これを実装するのはさすがに面倒。また、variants() に追加忘れたとしてもコンパイラは何も言ってくれない。

pub enum Foo {
    A,
    B,
}

impl Foo {
    pub fn variants() -> impl Iterator<Item = Foo> {
        array::IntoIter::new([Self::A, Self::B])
    }
}

impl fmt::Display for Foo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Foo::A => write!(f, "a"),
            Foo::B => write!(f, "b"),
        }
    }
}

impl FromStr for Foo {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::variants()
            .find(|x| x.to_string() == s)
            .ok_or_else(|| format!("Unknown option: `{}`", s))
    }
}
Takanori IshikawaTakanori Ishikawa

thiserror のように使える crate があると便利かも?

use strenum::StringEnum;

#[derive(StringEnum, Debug)]
pub enum Vegetable {
    #[str("Potato")]
    Potato,
    #[str("Broccori")]
    Broccori,
    #[str("Carrot")]
    Carrot,
}

フィールドを持つ enum もできないだろうか?

use strenum::StringEnum;

#[derive(StringEnum, Debug)]
pub enum ConnectionState {
    #[str("initializing")]
    Initializing,
    #[str("Connected({0})")]
    Connected(i32),
    #[str("Disconnected")]
    Disconnected,
}

最短マッチで文字列を読んで、FromStr を実装しているフィールドなら出来そう? すでにありそうだな。

Takanori IshikawaTakanori Ishikawa

strum 使ってみた。

use strum::IntoEnumIterator;

#[derive(
    Debug,
    PartialEq,
    Clone,
    Copy,
    strum_macros::EnumString,
    strum_macros::Display,
    strum_macros::IntoStaticStr,
    strum_macros::EnumIter,
)]
pub enum Color {
    #[strum(serialize = "red")]
    Red,
    #[strum(serialize = "blue")]
    Blue,
}

これだけで、Color::iter()Into<&'static str> が実装される。

let color_option_values: Vec<&'static str> = Color::iter().map(|o| o.into()).collect();
このスクラップは2021/10/17にクローズされました