Макросы, как сгенерировать вариант enum из литерала str?

У меня есть enum с более чем 100 вариантами. и я должен получить каждый из его вариантов из строки. Что-то вроде этого.

enum VeryLongEnum {
    A,
    B,
    C,
    D,
    E,
}

impl From<&'static str > for VeryLongEnum {
    fn from(s: &'static str) -> VeryLongEnum {
        match s {
            "A" => VeryLongEnum::A,
            "B" => VeryLongEnum::B,
            "C" => VeryLongEnum::C,
            "D" => VeryLongEnum::D,
            "E" => VeryLongEnum::E,
        }
    }
}

Но я не хочу писать все варианты один за другим, это бред.

Я пытаюсь создать мини-макрос, чтобы упростить это, я сделал что-то вроде этого.

macro_rules! from_str {
    ( $s:expr ) => {{
        let t: VeryLongEnum = VeryLongEnum::$s;
        t
    }};
}

использовать так.

let variant = "A";
let en = from_str!(variant);

Но у меня есть ошибка, которая говорит expected a identifier.

Я понимаю, что идентификаторы и выражения - это разные типы деревьев токенов, но вопрос в том, как я могу «заставить» это генерировать вариант перечисления с литералом?

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
let variant = "A";
let en = from_str!(variant);

variant — это строка, которая существует во время выполнения, вы не можете передать ее в такой макрос.

Альтернативой является определение макроса, который определяет перечисление, а также преобразование строки. Этот макрос может использовать макрос stringify! в Rust для преобразования идентификатора at в статическую строку, которую можно передать для сопоставления с образцом. Поскольку преобразование допускает ошибки, вы должны определить TryFrom вместо From (или FromStr, что позволяет вызывать "A".parse()).

macro_rules! go {
    ($($ident:ident)+) => {
        #[derive(Debug)]
        enum VeryLongEnum {
            $($ident,)+
        }

        impl TryFrom<&'static str> for VeryLongEnum {
            type Error = &'static str;

            fn try_from(s: &'static str) -> Result<VeryLongEnum, &'static str> {
                match s {
                    $(stringify!($ident) => Ok(VeryLongEnum::$ident),)+
                    _ => Err("Invalid String")
                }
            }
        }
    }
}

go! { A B C D E }

fn main() {
    let _ = dbg!(VeryLongEnum::try_from("A"));
    let _ = dbg!(VeryLongEnum::try_from("E"));
    let _ = dbg!(VeryLongEnum::try_from("F"));
}

Вывод:

[src/main.rs:24] VeryLongEnum::try_from("A") = Ok(
    A,
)
[src/main.rs:25] VeryLongEnum::try_from("E") = Ok(
    E,
)
[src/main.rs:26] VeryLongEnum::try_from("F") = Err(
    "Invalid String",
)

Детская площадка

Спасибо!, на самом деле у меня есть два очень длинных перечисления, поэтому я немного изменил его, чтобы добавить имя, а не писать макрос дважды модифицированный плейграунд

al3x 14.10.2022 14:35

Другие вопросы по теме