我有Java背景,我可能有enum Direction { NORTH, SOUTH, EAST, WEST}这样的代码,我可以使用增强的for循环依次处理每个值,例如:
enum Direction { NORTH, SOUTH, EAST, WEST}
for(Direction dir : Direction.values()) { //do something with dir }
我想对Rust枚举做类似的事情。
3bygqnnd1#
可以使用strum crate轻松地迭代枚举的值。
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
blmhpbnm2#
如果枚举类似于C(如示例中所示),则可以为每个变量创建一个static数组,并返回引用的迭代器:
static
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来得到一个值的迭代器:
Copy
Iterator::copied
impl Trait
impl Direction { pub fn iterator() -> impl Iterator<Item = Direction> { [North, South, East, West].iter().copied() } }
另见:
qvtsj1bj3#
不,没有。我认为这是因为Rust中的枚举比Java中的强大得多--它们实际上是成熟的algebraic data types。例如,你期望如何迭代下面的枚举的值:
enum Option<T> { None, Some(T) }
?它的第二个成员Some不是静态常量-您可以使用它来创建Option<T>的值:
Some
Option<T>
let x = Some(1); let y = Some("abc");
因此,没有合理的方法可以迭代 any enum的值。当然,我认为,有可能在编译器中添加对 static 枚举(即只有静态项的枚举)的特殊支持,因此它会生成一些函数,返回枚举的值或带有它们的静态向量,但我认为编译器中额外的复杂性是不值得的。如果确实需要此功能,可以编写一个自定义语法扩展(参见this问题).这个扩展应该接收一个标识符列表,并输出一个枚举和一个静态常量向量,这些标识符作为内容.一个常规的宏在某种程度上也会起作用,但据我所知,您不能用多重性转录宏参数两次,因此您必须手动编写两次枚举元素,这很不方便。这个问题也可能引起一些兴趣:#5417当然,你也可以编写代码,手工返回枚举元素的列表。
vd8tlhqk4#
我在crate plain_enum中实现了基本功能。它可用于声明类似C的枚举,如下所示:
plain_enum
#[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) })
j2qf4p5b5#
可以使用关联的常量:
impl Direction { const VALUES: [Self; 4] = [Self::NORTH, Self::SOUTH, Self::EAST, Self::WEST]; } fn main() { for direction in Direction::VALUES.iter().copied() { todo!(); } }
vsikbqxv6#
如果你不想导入一个第三方的板条箱,你可以自己做一个宏来实现,下面是我实现它的方法(可能有很多方法可以改进):
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), }
由于Rust中枚举的强大功能,很难实现完全可迭代的枚举,因为它们不像C或Java中的简单枚举。
lndjwyie7#
enum-iterator crate有助于迭代枚举器。
wh6knrhe8#
以下是我对“艾哈迈德·梅雷兹”回答的看法:
vis
error: repetition matches empty token tree
#[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); } }
az31mfrm9#
我对“koral的回答”的变体
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(...); }
9条答案
按热度按时间3bygqnnd1#
可以使用strum crate轻松地迭代枚举的值。
输出:
blmhpbnm2#
如果枚举类似于C(如示例中所示),则可以为每个变量创建一个
static
数组,并返回引用的迭代器:如果你让枚举实现
Copy
,你可以使用Iterator::copied
并返回impl Trait
来得到一个值的迭代器:另见:
qvtsj1bj3#
不,没有。我认为这是因为Rust中的枚举比Java中的强大得多--它们实际上是成熟的algebraic data types。例如,你期望如何迭代下面的枚举的值:
?
它的第二个成员
Some
不是静态常量-您可以使用它来创建Option<T>
的值:因此,没有合理的方法可以迭代 any enum的值。
当然,我认为,有可能在编译器中添加对 static 枚举(即只有静态项的枚举)的特殊支持,因此它会生成一些函数,返回枚举的值或带有它们的静态向量,但我认为编译器中额外的复杂性是不值得的。
如果确实需要此功能,可以编写一个自定义语法扩展(参见this问题).这个扩展应该接收一个标识符列表,并输出一个枚举和一个静态常量向量,这些标识符作为内容.一个常规的宏在某种程度上也会起作用,但据我所知,您不能用多重性转录宏参数两次,因此您必须手动编写两次枚举元素,这很不方便。
这个问题也可能引起一些兴趣:#5417
当然,你也可以编写代码,手工返回枚举元素的列表。
vd8tlhqk4#
我在crate
plain_enum
中实现了基本功能。它可用于声明类似C的枚举,如下所示:
然后将允许您执行以下操作:
j2qf4p5b5#
可以使用关联的常量:
vsikbqxv6#
如果你不想导入一个第三方的板条箱,你可以自己做一个宏来实现,下面是我实现它的方法(可能有很多方法可以改进):
然后你就可以:
此实现仅限于简单枚举。请考虑以下枚举,您将如何对其进行迭代?
由于Rust中枚举的强大功能,很难实现完全可迭代的枚举,因为它们不像C或Java中的简单枚举。
lndjwyie7#
enum-iterator crate有助于迭代枚举器。
wh6knrhe8#
以下是我对“艾哈迈德·梅雷兹”回答的看法:
vis
,因为Rust不喜欢错误为error: repetition matches empty token tree
的空可见性参数),包括:az31mfrm9#
我对“koral的回答”的变体
vis
into_iter