如何在 Vec<T> 中获取枚举的所有变体?

How to get all the variants of an enum in a Vec<T>?

我想知道如何为 return Vec<T> 或某种集合类型中的所有变体的任何枚举实现一个方法。类似于:

pub enum MyEnum {
    EnumVariant1
    EnumVariant2
    ...
}

impl MyEnum {
    fn values(&self) -> Vec<MyEnum> {
        // do Rust stuff here
    }
}

没有智能和标准的解决方案。

一个明显的方法是通过重复变量自己声明数组:

static VARIANTS: &[MyEnum] = &[
    MyEnum::EnumVariant1,
    MyEnum::EnumVariant2,
];

这是一个合理的解决方案。当您的代码不断发展时,您经常会发现您不想在静态数组中使用 all 变体。或者您需要多个数组。

或者,如果有很多元素或多个枚举,您可以为此创建一个宏:

macro_rules! make_enum {
    (
        $name:ident $array:ident {
            $( $variant:ident, )*
        }
    ) => {
        pub enum $name {
            $( $variant, )*
        }
        static $array: &[$name] = &[
            $( $name::$variant, )*
        ];
    }
}

make_enum! (MyEnum VARIANTS {
    EnumVariant1,
    EnumVariant2,
});

make_enum! 宏调用将创建枚举和名为 VARIANTS 的静态数组。

或者,您可以使用 proc 宏,使其通用(受 How to get the number of elements (variants) in an enum as a constant value? 启发):

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(AllVariants)]
pub fn derive_all_variants(input: TokenStream) -> TokenStream {
    let syn_item: syn::DeriveInput = syn::parse(input).unwrap();

    let variants = match syn_item.data {
        syn::Data::Enum(enum_item) => {
            enum_item.variants.into_iter().map(|v| v.ident)
        }
        _ => panic!("AllVariants only works on enums"),
    };
    let enum_name = syn_item.ident;

    let expanded = quote! {
        impl #enum_name {
            pub fn all_variants() -> &'static[#enum_name] {
                &[ #(#enum_name::#variants),* ]
            }
        }
    };
    expanded.into()
}

那么你可以这样使用它:

use all_variants::AllVariants;

#[derive(AllVariants, Debug)]
enum Direction {
    Left,
    Top,
    Right,
    Bottom,
}

fn main() {
    println!("{:?}", Direction::all_variants());
}

输出:

[Left, Top, Right, Bottom]

但正如@Denys Séguret 在之前的评论中所写,还有很多工作要做:

  • proc 宏必须驻留在它们自己的 crate 中(如果您想在 crates.io 上发布您的作品,则 proc macro crate 也必须发布);
  • 它需要 synquote 过程宏中的箱子(如果不想重新发明轮子的话)。