Как получить количество элементов в перечислении в виде постоянного значения?

Есть ли способ извлечь количество элементов в перечислении?

Простой пример (с мнимым number_of_elements метод):

enum FooBar { A = 0, B, C, };

println!("Number of items: {}", FooBar.number_of_elements());
// "Number of items: 3"

В Си я бы обычно делал...

enum FooBar { A = 0, B, C, };
#define FOOBAR_NUMBER_OF_ITEMS (C + 1)

Однако эквивалент Rust для этого не работает:

enum FooBar { A = 0, B, C, };
const FOOBAR_NUMBER_OF_ITEMS: usize = (C as usize) + 1;

// Raises an error:
//     unimplemented constant expression: enum variants

Включение последнего элемента в перечисление очень неудобно, потому что сопоставление перечислений приведет к ошибке, если не все участники учтены.

enum FooBar { A = 0, B, C, FOOBAR_NUMBER_OF_ITEMS, };

Есть ли способ получить количество элементов в перечислении в виде постоянного значения?


Примечание: даже если это не имеет прямого отношения к вопросу, причина, по которой я хотел эту функцию, заключается в том, что я использую шаблон компоновщика для создания последовательности действий, которые имеют смысл запускать только один раз. По этой причине я могу использовать массив фиксированного размера размером перечисления.

3 ответа

Обновление от 2022 г.

В версии rust nightly появилась новая функция .

Пример для использования в std::mem::variant_countrust docs .

      use std::mem;

enum Void {}
enum Foo { A(&'static str), B(i32), C(i32) }

assert_eq!(mem::variant_count::<Void>(), 0);
assert_eq!(mem::variant_count::<Foo>(), 3);

assert_eq!(mem::variant_count::<Option<!>>(), 2);
assert_eq!(mem::variant_count::<Result<!, !>>(), 2);

Вы можете использовать новые процедурные макросы (стабильные через 2 недели на дату написания этого ответа):

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

use proc_macro::TokenStream;

#[proc_macro_derive(EnumVariantCount)]
pub fn derive_enum_variant_count(input: TokenStream) -> TokenStream {
    let syn_item: syn::DeriveInput = syn::parse(input).unwrap();
    let len = match syn_item.data {
        syn::Data::Enum(enum_item) => enum_item.variants.len(),
        _ => panic!("EnumVariantCount only works on Enums"),
    };
    let expanded =quote! {
const LENGTH: usize = #len;
    };
    expanded.into()
}

Это оставлено в качестве упражнения для читателя, чтобы гарантировать, что этот производный макрос может использоваться несколько раз в одном и том же модуле.

Чтобы использовать макрос, просто прикрепите #[derive(EnumVariantCount)] к вашему перечислению. Теперь должна быть глобальная константа с именем LENGTH,

В качестве альтернативы (возможно, это новее, чем исходный ответ), вы можете использовать ящик для бренчания и использовать макрос EnumCount. Вот их пример :

      use strum::{EnumCount, IntoEnumIterator};
use strum_macros::{EnumCount as EnumCountMacro, EnumIter};

#[derive(Debug, EnumCountMacro, EnumIter)]
enum Week {
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
}

assert_eq!(7, Week::COUNT);
assert_eq!(Week::iter().count(), Week::COUNT);
Другие вопросы по тегам