Rust中能否为枚举变体关联常量并在编译时自动收集这些值?
稳定版Rust中枚举关联字符串并自动生成静态数组的实现方案
在稳定版Rust里,有两种优雅的编译时实现方式,分别适配不同场景:
一、声明宏方案(轻量无依赖)
适合结构简单的枚举,无需额外依赖,直接通过声明宏同时定义枚举和静态数组:
// 辅助宏:统计枚举变体数量 macro_rules! variant_count { () => (0); ($_head:ident, $($tail:ident),*) => (1 + variant_count!($($tail),*)); } // 核心宏:定义枚举并生成对应的静态字符串数组 macro_rules! enum_with_str_array { ($vis:vis enum $enum_name:ident { $($variant:ident $(($($field_ty:ty),*))? => $str_val:literal,)* }) => { $vis enum $enum_name { $($variant $(($($field_ty),*))?,)* } $vis const $enum_name:ident _STR_ARRAY: [&'static str; variant_count!($($variant),*)] = [ $($str_val,)* ]; }; }
使用示例
enum_with_str_array! { enum MyEnum { A => "foo", B(String) => "bar", C(i32) => "baz", } } fn main() { println!("{:?}", MYENUM_STR_ARRAY); // 输出: ["foo", "bar", "baz"] }
该方案完全基于稳定版特性,无需引入外部依赖,编译时直接展开代码,实现成本极低。
二、派生宏(属性宏)方案(灵活可扩展)
如果枚举结构复杂(比如需要结合其他派生 trait、变体包含复杂字段),可以用过程宏实现更灵活的标注与代码生成:
步骤1:添加依赖
在Cargo.toml中添加过程宏所需依赖:
[package] name = "enum_str_gen" version = "0.1.0" edition = "2021" [dependencies] syn = { version = "2.0", features = ["full"] } quote = "1.0" [lib] proc-macro = true
步骤2:实现派生宏
在src/lib.rs中编写宏代码:
use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, Data, DeriveInput, Meta, NestedMeta}; #[proc_macro_derive(StrArray, attributes(str))] pub fn derive_str_array(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let enum_name = &input.ident; let enum_vis = &input.vis; // 解析枚举变体及对应的`#[str("...")]`属性 let mut str_values = Vec::new(); let data = match &input.data { Data::Enum(data) => data, _ => panic!("StrArray 仅支持枚举类型"), }; for variant in &data.variants { let mut str_val = None; // 遍历变体属性,提取`#[str("xxx")]`中的字符串 for attr in &variant.attrs { if let Ok(Meta::List(list)) = attr.parse_meta() { if list.path.is_ident("str") { for nested in &list.nested { if let NestedMeta::Lit(syn::Lit::Str(s)) = nested { str_val = Some(s.value()); break; } } } } } let str_val = str_val.expect(&format!("变体 {} 必须添加 #[str(\"...\")] 属性", variant.ident)); str_values.push(str_val); } // 生成静态数组的标识符(枚举名大写加后缀) let arr_name = syn::Ident::new( &format!("{}_STR_ARRAY", enum_name.to_string().to_uppercase()), enum_name.span(), ); let arr_len = str_values.len(); // 生成最终代码:原枚举 + 静态数组 let expanded = quote! { #input #enum_vis const #arr_name: [&'static str; #arr_len] = [ #(#str_values,)* ]; }; expanded.into() }
使用示例
在src/main.rs中使用派生宏:
use enum_str_gen::StrArray; #[derive(Debug, StrArray)] enum MyEnum { #[str("foo")] A, #[str("bar")] B(String), #[str("baz")] C(i32), } fn main() { println!("{:?}", MYENUM_STR_ARRAY); // 输出: ["foo", "bar", "baz"] }
该方案扩展性强,可轻松结合其他派生 trait,代码结构更清晰,适合复杂项目场景。
内容的提问来源于stack exchange,提问作者Jack Stodart
相关产品推荐
相关产品推荐

