如何为含26个变体的Alphabert枚举批量实现EnumString?
批量实现枚举变体的大小写不敏感序列化(strum crate)
问题背景
我使用strum crate的EnumString trait实现String到枚举变体的转换,定义了包含A到Z共26个变体的Alphabet枚举。不想手动给每个变体添加#[strum(serialize = "A", serialize = "a")]这类重复属性,尝试编写宏批量生成时,编译器报错提示需要字符串字面量——因为宏里的$variant标记是标识符类型,不是&str,无法直接生成大小写形式。后来通过给枚举添加#[strum(serialize_all = "UPPERCASE")]和#[strum(ascii_case_insensitive)]属性,完美解决了问题。
解决方案1:自定义宏实现(解决宏报错问题)
要在宏里把变体标识符转成字符串字面量,需要用Rust内置的stringify!宏。注意属性中不能直接调用to_lowercase()这类方法(属性只支持常量表达式),可以通过嵌套宏生成大小写字符串:
use strum::EnumString; use strum_macros::EnumString; // 生成单个变体及其大小写序列化属性的宏 macro_rules! alphabet_variant { ($variant:ident) => { #[strum(serialize = stringify!($variant), serialize = stringify!($variant).to_ascii_lowercase())] $variant, }; } // 批量生成所有变体的宏 macro_rules! alphabet_enum { ($($variant:ident),*) => { #[derive(Debug, EnumString)] enum Alphabet { $(alphabet_variant!($variant))* } }; } // 生成A-Z的枚举变体 alphabet_enum!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
不过这种方式需要处理常量表达式的限制,相对繁琐,更推荐下面的内置属性方案。
解决方案2:使用strum内置属性(更简洁)
strum已经提供了现成的属性来简化这个需求,完全不需要自定义宏:
use strum::EnumString; use strum_macros::EnumString; #[derive(Debug, EnumString)] #[strum(serialize_all = "UPPERCASE")] // 将所有变体的序列化值统一设为大写 #[strum(ascii_case_insensitive)] // 反序列化时忽略ASCII大小写 enum Alphabet { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z } // 测试验证 fn main() { let lower_a = Alphabet::from_str("a").unwrap(); let upper_a = Alphabet::from_str("A").unwrap(); assert_eq!(lower_a, upper_a); // 大小写输入都能匹配到同一个变体 }
属性说明:
serialize_all = "UPPERCASE":自动将每个变体的名称转为大写作为序列化字符串,无需手动指定每个变体的serialize属性。ascii_case_insensitive:开启后,反序列化时会忽略输入字符串的ASCII大小写,直接匹配对应的变体。
内容的提问来源于stack exchange,提问作者Jim
相关产品推荐
相关产品推荐

