Issue by ghost
_Sunday Mar 17, 2013 at 02:56 GMT_
_For earlier discussion, see https://github.com/rust-lang/rust/issues/5417_
_This issue was labelled with: A-libs, A-traits, B-RFC in the Rust repository_
Since I've been doing some work with the deriving code in libsyntax, I thought I'd solicit opinions on adding a new trait to libcore:
trait Enum {
static fn enumerate(blk: &fn(Self) -> bool);
}
Obviously, this would be most useful for enums with only nullary variants, but other enumerable types could take advantage of it as well (bool::all_values currently serves the same purpose).
This would make it possible to write
// enum Dir { North, East, South, West }
for Enum::enumerate |dir: Dir| { ... }
instead of
for [North, East, South, West].each |dir| { ... }
A standard implementation for enums with only nullary variants would be made available through #[deriving(Enum)], and a default method (or utility trait/impl until those are working) would be provided for obtaining all the values in a vector. It might be beneficial to have a static fn cardinality() -> Option<uint> method on the trait as well.
If the trait name is too easily confused with the keyword, another option is Enumerable.
Hello, apologies for reviving an old entry (I hope I'm not breaking any rules): what is the latest state of affairs on this subject? Are there (any plans for) traits implementable for Enums that allow you to access the tag number (preferably without consuming the enum in the process)?
My use case is a piece of code that can take any type (which it assumes/hopes to be an enum), and internally allocates an array whose length is the number of variants of the enum type - that is to say for:
enum Something { A, B, C }
An array of 3 would be allocated. To do this I have put together a trait "EnumTag" which provides the methods "tag_number(&self) -> usize" and "max_tag_number() -> usize". I thought this would be relatively trivial to implement, but then it turns out that (using the previous enum as an example):
impl EnumTag for Something {
fn tag_number(&self) -> usize { *self as usize }
fn max_tag_number() -> usize { Something::C as usize }
}
That implementation of tag_number is now illegal, as "as usize" now consumes self, whereas from old code examples online (and prior experience) I'm lead to believe that it used to be possible to write the above?
But anyway, I digress: are there any plans/thoughts on automatically derivable traits for enums that provide features such as the EnumTag example above? (or similar?)
Please let me know if this is the wrong place to ask.
You could self.clone() but I don't think that'd work as you want it to, either way :/
@omaskery You just need to #[derive(Copy, Clone)] for that code to compile. Rust doesn't know that your enum is plain old data and that it can pass it by value, rather than consuming it.
Otherwise, you're on the right track, and what you're doing should compile on stable Rust today (the array you're trying to allocate will have to be a Vec).
@withoutboats Fantastic! I'll try that now, thank you.
And @Enamex, thank you both for taking the time to answer!
On a similar note, I stumbled upon https://github.com/rust-lang/rfcs/blob/master/text/0639-discriminant-intrinsic.md moments after posting my initial comment (how silly I felt!) so that's interesting, too :)
I find myself wanting to iterate over the variants of enums often, I'd love to see this get into Rust.
Same here; in addition, I often need functionalities like Haskell's pred and succ, but have to resort to workarounds instead.
You can do this with rust-enum-derive. See #[DeriveIterVariants(..)]
custom_derive! {
#[derive(Debug, PartialEq, Eq, IterVariants(CandyVariants))]
pub enum Candy { Musk, FruitRock, BoPeeps, LemonSherbert }
}
Links:
Just my 2 cents: For a personal project, I needed a similar thing, which I encapsulated into an own crate, plain_enum (see here).
While it is probably not suited for everyone's needs, it may serve as a starting point or inspiration for others.
It can be used to declare a C-like enum as follows:
#[macro_use]
extern crate plain_enum;
plain_enum_mod!(module_for_enum, EnumName {
Val1,
Val2,
Val3,
});
And will then allow you to do things like the following:
for value in EnumName::values() {
// do things with value
}
let enummap = EnumName::map_from_fn(|value|
convert_enum_value_to_mapped_value(value)
)
In the domain I work in, data modeling, we need to represent each of the elements that exists in our system's universe. I'd like to make these elements an enum so that I can have verification that all possibilities are covered. But in order to do this, I need to be able to iterate through the possibilities and to count them, so that I can store my measurements, for example, in a packed array of doubles. This use case is treating the enum to make a static, perfect hash that also is typechecked.
For now, you can write a macro which implements that iterator as well as the enum, I've written something similar before. Here's an example: https://is.gd/1hw4Tn
Am I correct in understanding that this issue has not had an RFC written for it in the currently expected manner, and that if one was written then iterable enums have a better chance of making it intro Rust as a built-in feature?
The alternatives suggested above work for enums where the variants have no payload:
enum Foo {
NoPayloadA,
NoPayloadB,
}
as opposed to
enum ByteFoo {
BytePayloadA(u8),
BytePayloadB(u8),
}
but given that we can iterate over the values of a given enum, then we should be able to iterate over enum with payloads that can be iterated over.
For example:
enum Foo {
FooA,
FooB,
}
enum Bar {
BarA,
BarB,
}
enum Baz {
BazFoo(Foo),
BazBar(Bar)
}
if given the preceding code we could iterate over Foo and Bar with something like Foo::iter() and Bar::iter(), then we should also be able to iterate over all four values of Baz with something like Baz::iter().
In fact, it seems like if this was implemented in such a way that built-in types could be iterated over as well, then all 512 possible values of my ByteFoo example above should then be able to be iterated over just as easily.
Since iteration is only possible on enums where every variant has the same type, if this were to be a built-in feature in standard Rust it would make changing a variant's type a potentially breaking API change even on variants that no one is supposed to use. For that reason, I believe this feature should be opt-in via some kind of #[derive(...)] annotation, the same way Copy, Clone, Default, etc require a #[derive(...)] annotation because they represent important commitments in your API that can't be removed without breaking code.
The status quo is that custom derive macros have been written to achieve this for no-payload enums. Is there any reason a similar custom derive macro could not be written for all same-payload enums? I don't know of any fundamental reason why they shouldn't be able to do that.
Some additional possible functionality for this trait:
trait Enum {
// Total enum items count
fn len(&self) -> usize;
// call of std::mem::discriminant() with additional typecheck.
fn ordinal(item: Self::Item) -> usize;
}
I'm a new Rust user, hopefully it's appropriate to add my 2垄 here!
It seems like Rust enums are really algebraic sum types, which is great, but C-style or particularly Java-style enums (symbolic names for a set of hard-coded constants) are not very well served. Java's Enum classes are excellent. Rust could really use something similar.
For the basic functionality, there's "custom discriminants for field-less enumerations" -- tucked away in the reference and not even mentioned in the Rust Book. It took me a while to find this!
The big missing features are conversion to and from strings, and iteration through all the constants. Iteration is the most important because it can be used to implement string conversions (i.e. iterate through the enum values to build a lookup table).
There are various crates available for doing various bits of this, but it seems like such a key feature that it would be good to pull this into the standard library. If there were a standard #[derive EnumIterator] or some such that would be great. Maybe that would just mean blessing one of the existing third-party solutions.
Most helpful comment
I find myself wanting to iterate over the variants of enums often, I'd love to see this get into Rust.