我来自Java的背景,可能会像这样定义一个枚举类型enum Direction { NORTH, SOUTH, EAST, WEST}
,然后使用增强for循环对每个值进行操作:
for(Direction dir : Direction.values()) {
//do something with dir
}
我希望能够使用Rust枚举来做类似的事情。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);
}
}
输出:
NORTH
SOUTH
EAST
WEST
如果枚举是类C的(就像您的示例一样),那么您可以创建每个变量的静态
数组,并返回对它们引用的迭代器:
如果枚举类似于C语言中的枚举类型(如您提供的示例),则可以创建一个包含每个枚举变量的静态数组,并返回该数组元素的引用迭代器:
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);
}
}
如果您使枚举实现Copy
,则可以使用Iterator :: copied
并返回 impl Trait
以获得值的迭代器:
impl Direction {
pub fn iterator() -> impl Iterator<Item = Direction> {
[North, South, East, West].iter().copied()
}
}
另请参阅:
iterator()
方法,那么就会出现错误。 strum
(或类似的库)可以解决这个问题。 - TheOperatorenum
的静态映射,所以我个人认为这仍然是一个不错的方法。如果有人想知道use self::Direction::*;
是什么意思,那是为了将enum
值带入命名空间,因此如果您愿意,可以删除它并将[North...
替换为[Direction :: North...
。 - abeenum Option<T> {
None,
Some(T)
}
?
它的第二个成员Some
不是静态常量,你可以使用它来创建Option<T>
类型的值:
let x = Some(1);
let y = Some("abc");
我在crate plain_enum
中实现了基本功能。
它可以用于声明类似于C语言的枚举,如下所示:
#[macro_use]
extern crate plain_enum;
plain_enum_mod!(module_for_enum, EnumName {
EnumVal1,
EnumVal2,
EnumVal3,
});
然后将允许您执行以下操作:
for value in EnumName::values() {
// do things with value
}
let enummap = EnumName::map_from_fn(|value| {
convert_enum_value_to_mapped_value(value)
})
impl Direction {
const VALUES: [Self; 4] = [Self::NORTH, Self::SOUTH, Self::EAST, Self::WEST];
}
fn main() {
for direction in Direction::VALUES.iter().copied() {
todo!();
}
}
如果您不想导入第三方的crate,您可以编写自己的宏来实现。以下是我如何实现的(可能有改进的方法):
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),*)
};
}
然后您可以执行以下操作:
iterable_enum!(pub, EnumName, Value1, Value2, Value3);
fn main() {
for member in EnumName::iterate() {
// ...
}
}
该实现仅限于简单的枚举类型。考虑以下枚举类型,您如何迭代它?:
enum MoreComplexEnum<T1, T2> {
One(T1),
Two(T2),
Other,
Both(T1, T2),
Error(String),
}
#[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))}
}
这是我对@Ahmed Merez的答案的看法:
vis
,因为Rust似乎不喜欢带有错误的空可见性参数error: repetition matches empty token tree
),
包括:
#[macro_export]
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
}
/// https://dev59.com/4GEi5IYBdhLWcg3wWLES#64678145
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 答案的改进
vis
into_iter
,返回真正的迭代器而非数组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(...);
}
Enum
特性的问题(https://github.com/mozilla/rust/issues/5417),可能可以派生出类似for dir in Direction::values() { ... }
的东西(也许)。 - huon