2017-01-13 23 views
7

Enum öğelerinin sayısını çıkarmak için bir yol var mı? (Hayali number_of_elements yöntemiyle)Enum öğelerinin sayısı sabit değer olarak nasıl alınır?

Basit bir örnek:

Normalde yapardım C'de
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) 

Buna Pas eşdeğer çalışmıyor Ancak:

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

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

Enumdaki son öğe çok uygunsuz çünkü eşleştirme enumları hata yapacaktır eğer tüm üyeler hesaplanmazsa.

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

Enumdaki öğelerin sayısını sabit değer olarak almanın bir yolu var mı?


Not: Bu yol, söz ilişkili olmasa bile, ben bu özelliği istiyordum nedeni sadece bir kez çalıştırmak için mantıklı bir dizi eylem oluşturmak için builder-pattern kullanıyorum olduğunu. Bu nedenle enumun boyutunu sabit boyutlu bir dizi kullanabilirim.

+0

Bunun faydalı olacağını düşündüğüm bir özellik. Sadece ilgi alanı: Neden varyasyon sayısına ihtiyacın var? –

+0

Bu özelliği neden aradığımı açıklayan bir açıklama eklendi. – ideasman42

+2

Dürüst olmak gerekirse, C ve C++ 'da sakıncalı buluyorum çünkü o zaman kodumu kirleten' switch'imdeki durumları ele almam gerekiyor. C ve C + + Ben de yararlı olan bu boyutu da dahil olmak üzere birkaç alternatif şeyler ilan enumlar (siyah sihirbazlık ...) ilan için bir makro kullanarak hile. Sanırım bir pas, Rust'ta da işe yarayacaktı ... ama daha iyi bir şey için umuyordum ... (özel bir türetme muhtemelen hile de yapabilir, sanırım ...) –

cevap

9

Sen (bu cevabı yazma tarihte 2 hafta içinde kararlı) yeni usule makroları kullanabilirsiniz: Bu okuyucuya bir excercise olarak bırakılır

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::parse_macro_input(&input.to_string()).unwrap(); 
    let len = match syn_item.body { 
     syn::Body::Enum(variants) => variants.len(), 
     _ => panic!("Every type other than an enum has exactly 1 (one) variant, please just use that value instead of wasting CPU cycles ;)"), 
    }; 
    quote! { 
const LENGTH: usize = #len; 
    }.unwrap().parse().unwrap() 
} 

bu derived makro kullanılabilir olmasını sağlamak için Aynı modül içinde birden çok kez.

Makroyu kullanmak için, numaranıza #[derive(EnumVariantCount)]'u iliştirmeniz yeterlidir. Şimdi LENGTH isimli global bir sabit olmalı.

+1

Harika! Ama egeramları idare etmenin doğru yolu (şu an) en iyi olmayan bir beden gibi, panik yapmak gibi. Makroları öğrenmek için [bu yazıyı] çok seviyorum (https://cbreeden.github.io/Macros11/). –

+0

Neden enkazda panik yapalım? Tek bir varyant enum ve bir tuple yapısı arasında fark yoktur. –

+2

Tamam, belki yeterince düşünmedim. Ama en azından * Ben * bir yapı tanımına '# [türetme (EnumVariantCount)]' ekleme derleme zaman hatasıyla sonuçlanmasını bekler. –

İlgili konular