153

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.

Chen Levy
  • 15,438
  • 17
  • 74
  • 92
dougli1sqrd
  • 1,653
  • 2
  • 10
  • 8
  • There's [an issue about an `Enum` trait](https://github.com/mozilla/rust/issues/5417) which would possibly be able to be derived to give something like `for dir in Direction::values() { ... }` (maybe). – huon Jan 27 '14 at 13:32

10 Answers10

154

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
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jordan Mack
  • 8,223
  • 7
  • 30
  • 29
  • 2
    There is also [the enum-iterator crate](https://crates.io/crates/enum-iterator), which provides similar functionality via `Sequence` trait and `all` function, with an advantage of supporting nested enums. – Tad Lispy Sep 28 '22 at 11:13
  • 1
    @abe That's a Playground problem. It only supports the top 100 crates. You can see `strum` working on [Rust Explorer](https://www.rustexplorer.com/b/y1lk59). – John Kugelman Jun 13 '23 at 16:23
61

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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
A.B.
  • 15,364
  • 3
  • 61
  • 64
  • This actually seems to probably be a better option than to use "enum" from rust for your needs. I actually wonder if ruts would have been better off calling their enums, unions or something else. Also, as a powerful variant, you can associate additional information like: [North(0), South(180), EAST(90), WEST(270)], etc, or more given the tuple help... – bluejekyll Aug 12 '15 at 00:10
  • 3
    The problem with this approach is that you need to keep the array in sync with the enum constants. If you add a constant and forget to adjust the `iterator()` method, you will have a bug. `strum` (or a similar library) addresses this. – TheOperator Dec 06 '21 at 19:36
  • 1
    @TheOperator's comment is fair, although I will add that I find myself, for various reasons (like performance, or for flexibility with higher-order functions / closures) sometimes keeping a static mapping to an `enum` around, so personally I think this is still a nice approach. In case anyone else is wondering about `use self::Direction::*;` that is there to bring the `enum` values into the namespace, so if you'd like you can remove that and replace `[North...` with `[Direction::North...` – abe Feb 20 '23 at 19:04
57

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • This is helpful. I think the easiest thing for me to do is maybe write an impl of my enum that returns a list of the values or an iterator or something. Thanks! – dougli1sqrd Jan 27 '14 at 08:23
  • 83
    I respectfully disagree that enumerated enums are not worth it. That this is a common and useful thing to do is _why they are called enums_! The algebraic power is great to have, of course, but convenience for the most common case is good too. – Rex Kerr Jan 27 '14 at 20:53
  • 1
    @RexKerr, that was just my suggestion about the reasons why this feature is still not there. Halted discussion in the corresponding issue supports my point, I believe. BTW, when I needed to do something with all enum elements at once, it usually was some kind of index (e.g. hash map) which allowed to retrieve enum value by some key, and this index should be constructed manually anyway. – Vladimir Matveev Jan 28 '14 at 06:58
  • 3
    @VladimirMatveev - Eh, very little should be constructed manually which can be constructed automatically. Kind of the point of having compilers, no? But I agree with your analysis. – Rex Kerr Jan 28 '14 at 19:29
  • It would be worth it. And an instantiated enum Option could be iterated over just fine. – stimulate Jan 14 '21 at 22:01
11

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)
})
phimuemue
  • 34,669
  • 9
  • 84
  • 115
8

You can use an associated constant:

impl Direction {
    const VALUES: [Self; 4] = [Self::NORTH, Self::SOUTH, Self::EAST, Self::WEST];
}

fn main() {
    for direction in Direction::VALUES.iter().copied() {
        todo!();
    }
}
cambunctious
  • 8,391
  • 5
  • 34
  • 53
2

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
2

The enum-iterator crate helps iterating enumerators.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Seeker
  • 75
  • 1
  • 5
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 20 '22 at 02:35
0

Here's my take on @Ahmed Merez's answer that:

  • doesn't allocate on the heap
  • is const
  • accepts (almost) an actual enum as input (requires vis cause Rust doesn't seem to like an empty visibility parameter with an error of error: repetition matches empty token tree) including:
    • derive input
    • attribute properties
#[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);
    }
}
koral
  • 525
  • 7
  • 23
0

My variation for @koral's answer

  • doesn't require vis
  • implements into_iter which returns an actual iterator instead of an array
macro_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(...);
}

mahor1221
  • 1
  • 1
0

I didn't see this in other answers so I figures this might be useful to be mentioned:

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.

Dávid Tóth
  • 2,788
  • 1
  • 21
  • 46