Reputation: 2003
I come from a Java background and I might have something like enum Direction { NORTH, SOUTH, EAST, WEST}
and I could do something with each of the values in turn with the enhanced for loop like:
for(Direction dir : Direction.values()) {
//do something with dir
}
I would like to do a similar thing with Rust enums.
Upvotes: 188
Views: 118340
Reputation: 170
If enum elements have same size you can use this
#[repr(usize)]
enum SomeEnum {
X = 0,
Y = 1,
Z = 2,
}
pub fn iterate_enum(start: usize, end: usize) -> impl Iterator<Item=SomeEnum> {
unsafe {(start..end).map(|num| transmute(num))}
}
Upvotes: 3
Reputation: 8723
You can use the strum crate to easily iterate through the values of an enum.
use strum::IntoEnumIterator; // 0.17.1
use strum_macros::EnumIter; // 0.17.1
#[derive(Debug, EnumIter)]
enum Direction {
North,
South,
East,
West,
}
fn main() {
for direction in Direction::iter() {
println!("{:?}", direction);
}
}
Output:
North
South
East
West
Upvotes: 187
Reputation: 3235
I didn't see this in other answers so I figured this might be useful too:
enum Direction {
NORTH,
SOUTH,
EAST,
WEST,
}
impl Direction {
pub fn into_iter() -> core::array::IntoIter<Direction, 4> {
[
Direction::NORTH,
Direction::SOUTH,
Direction::EAST,
Direction::WEST,
]
.into_iter()
}
}
Yes, it is violates the DRY principle lightly, but on the other hand no external crates or fancy macros are needed for it, so it's much simpler.
Upvotes: 4
Reputation: 9552
You can use an associated constant:
#[derive(Debug)]
enum Direction { North, South, East, West }
impl Direction {
const VALUES: [Self; 4] = [Self::North, Self::South, Self::East, Self::West];
}
fn main() {
for direction in Direction::VALUES {
println!("{direction:?}");
}
}
Upvotes: 16
Reputation: 21
My variation for @koral's answer
vis
into_iter
which returns an actual iterator instead of an arraymacro_rules! iterable_enum {(
$(#[$derives:meta])*
$pub:vis enum $name:ident {
$(
$(#[$nested_meta:meta])*
$member:ident,
)*
}) => {
const _MEMBERS_COUNT:usize = iterable_enum!(@count $($member)*);
$(#[$derives])*
$pub enum $name {
$($(#[$nested_meta])* $member),*
}
impl $name {
pub fn into_iter() -> std::array::IntoIter<$name, _MEMBERS_COUNT> {
[$($name::$member,)*].into_iter()
}
}
};
(@count) => (0usize);
(@count $x:tt $($xs:tt)* ) => (1usize + iterable_enum!(@count $($xs)*));
}
fn main() {
iterable_enum! {
#[derive(Debug, serde::Deserialize)]
pub enum X {
#[serde(rename="a")]
A,
B,
}}
X::into_iter().fold(...);
}
Upvotes: 2
Reputation: 693
Here's my take on @Ahmed Merez's answer that:
vis
cause Rust doesn't seem to like an empty visibility parameter with an error of error: repetition matches empty token tree
)
including:
#[macro_export]
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
}
/// https://stackoverflow.com/a/64678145/10854888
macro_rules! iterable_enum {
($(#[$derives:meta])* $(vis $visibility:vis)? enum $name:ident { $($(#[$nested_meta:meta])* $member:ident),* }) => {
const count_members:usize = $crate::count!($($member)*);
$(#[$derives])*
$($visibility)? enum $name {
$($(#[$nested_meta])* $member),*
}
impl $name {
pub const fn iter() -> [$name; count_members] {
[$($name::$member,)*]
}
}
};
}
fn main() {
iterable_enum! {
#[derive(Debug, serde::Deserialize)]
vis pub(crate) enum X {
#[serde(rename="a")]
A,
B
}
}
for x in X::iter() {
dbg!(x);
}
}
Upvotes: 0
Reputation: 21
If you do not want to import a third-party crate, you can make your own macro to do so. Here is how I achieved it (there are probably ways to improve this):
macro_rules! iterable_enum {
($visibility:vis, $name:ident, $($member:tt),*) => {
$visibility enum $name {$($member),*}
impl $name {
fn iterate() -> Vec<$name> {
vec![$($name::$member,)*]
}
}
};
($name:ident, $($member:tt),*) => {
iterable_enum!(, $name, $($member),*)
};
}
And then you can do:
iterable_enum!(pub, EnumName, Value1, Value2, Value3);
fn main() {
for member in EnumName::iterate() {
// ...
}
}
This implementation is limited to simple enums. Consider the following enum, how would you iterate over it?:
enum MoreComplexEnum<T1, T2> {
One(T1),
Two(T2),
Other,
Both(T1, T2),
Error(String),
}
Because of the power of enums in Rust, it can be difficult to implement a perfectly iterable enum, since they are not like the simple enums you have in C or Java.
Upvotes: 2
Reputation: 16630
If the enum is C-like (as in your example), then you can create a static
array of each of the variants and return an iterator of references to them:
use self::Direction::*;
use std::slice::Iter;
#[derive(Debug)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn iterator() -> Iter<'static, Direction> {
static DIRECTIONS: [Direction; 4] = [North, South, East, West];
DIRECTIONS.iter()
}
}
fn main() {
for dir in Direction::iterator() {
println!("{:?}", dir);
}
}
If you make the enum implement Copy
, you can use Iterator::copied
and return impl Trait
to have an iterator of values:
impl Direction {
pub fn iterator() -> impl Iterator<Item = Direction> {
[North, South, East, West].iter().copied()
}
}
See also:
Upvotes: 83
Reputation: 35973
I implemented basic functionality in the crate plain_enum
.
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 {
EnumVal1,
EnumVal2,
EnumVal3,
});
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)
})
Upvotes: 11
Reputation: 127711
No, there is none. I think that is because enums in Rust are much more powerful than in Java - they are in fact full-fledged algebraic data types. For example, how would you expect to iterate over values of this enum:
enum Option<T> {
None,
Some(T)
}
?
Its second member, Some
, is not a static constant - you use it to create values of Option<T>
:
let x = Some(1);
let y = Some("abc");
So there is no sane way you can iterate over values of any enum.
Of course, I think, it would be possible to add special support for static enums (i.e. enums with only static items) into the compiler, so it would generate some function which return values of the enum or a static vector with them, but I believe that additional complexity in the compiler is just not worth it.
If you really want this functionality, you could write a custom syntax extension (see this issue). This extension should receive a list of identifiers and output an enum and a static constant vector with these identifiers as a content. A regular macro would also work to some extent, but as far as I remember you cannot transcript macro arguments with multiplicity twice, so you'll have to write enum elements twice manually, which is not convenient.
Also this issue may be of some interest: #5417
And of course you can always write code which returns a list of enum elements by hand.
Upvotes: 65