You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:43:08