如何在 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 也必须发布);
- 它需要
syn
和 quote
过程宏中的箱子(如果不想重新发明轮子的话)。
我想知道如何为 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 也必须发布);
- 它需要
syn
和quote
过程宏中的箱子(如果不想重新发明轮子的话)。