У меня есть 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
.
Я понимаю, что идентификаторы и выражения - это разные типы деревьев токенов, но вопрос в том, как я могу «заставить» это генерировать вариант перечисления с литералом?
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",
)
Спасибо!, на самом деле у меня есть два очень длинных перечисления, поэтому я немного изменил его, чтобы добавить имя, а не писать макрос дважды модифицированный плейграунд