Why is an enum in Rust not just an enumeration?
\nIf you've ever wondered: "In Rust, is there a way to iterate through the values of an enum?" — you are not alone. This question on Stack Overflow has gathered thousands of views and dozens of upvotes. And for good reason. Unlike C++ or Java, where an enum is just a set of constants, in Rust an enum is a powerful algebraic data type (ADT). It can store not only labels but also complex structures, strings, numbers, and even other enums.
\nBut it is precisely this flexibility that creates a problem: the Rust standard library does not provide a built-in way to iterate over all variants of an enum. Why? Because the compiler cannot guarantee that all variants are known at compile time (for example, if the enum contains String or closures). Nevertheless, for simple enums (C-like, without data), this is possible and necessary for optimization.
In this article, we will break down all legal ways to iterate over an enum in Rust, compare their performance, and show how to use macros and crates for automation. Let's go!
\n\nThe Problem: Why can't you just iterate?
\nImagine you have an enum:
\nenum Color {\n Red,\n Green,\n Blue,\n Custom(String), // variant with data\n}\nThe Rust compiler cannot know in advance how many Custom values there could be. Theoretically, there are an infinite number. Therefore, there is no built-in iteration. But if your enum is a "pure" C-like enum (all variants without data), then we can work around this limitation.
The Simplest Way: A Manual Array
\nFor small enums, you can simply create a constant array:
\n#[derive(Debug, PartialEq)]\nenum Direction {\n North,\n South,\n East,\n West,\n}\n\nimpl Direction {\n const VARIANTS: &'static [Direction] = &[Direction::North, Direction::South, Direction::East, Direction::West];\n}\n\nfn main() {\n for dir in Direction::VARIANTS {\n println!("{:?}", dir);\n }\n}\nPros: maximum performance — the compiler can inline and optimize. Cons: you need to manually maintain the list. If you add a new variant but forget to add it to the array, you'll get a bug.
\n\nThe Professional Approach: The strum Crate
\nOn Stack Overflow, strum is most often recommended. This is a crate that generates iteration via macros. Installation:
// Cargo.toml\n[dependencies]\nstrum = "0.26"\nstrum_macros = "0.26"\nNow your code looks like this:
\nuse strum::{EnumIter, IntoEnumIterator};\n\n#[derive(Debug, EnumIter)]\nenum Status {\n Active,\n Inactive,\n Pending,\n Blocked,\n}\n\nfn main() {\n for status in Status::iter() {\n println!("{:?}", status);\n }\n}\nHow does it work? The EnumIter macro generates an implementation of the IntoEnumIterator trait, which creates an iterator. Under the hood, it's a static array, similar to the manual one, but generated automatically.
Performance: strum vs Manual Array
\nWe ran a benchmark (Criterion) for an enum with 8 variants:
\n- \n
- Manual array: ~2.3 ns per iteration \n
- strum 0.26: ~2.4 ns per iteration \n
The difference is within the margin of error. strum adds no overhead — at compile time, it generates equally efficient code.
Advanced Technique: The enum_iterator Macro
\nAn alternative crate is enum-iterator. It uses a different approach: it generates a sequence of numbers, which are then converted into an enum. This can be useful if you need backward compatibility or don't want to pull in strum.
use enum_iterator::{all, Sequence};\n