在Rust中,是否有一种方法可以遍历枚举的值?


70

我来自Java背景,可能有类似的东西,enum Direction { NORTH, SOUTH, EAST, WEST}并且可以使用增强的for循环依次处理每个值:

for(Direction dir : Direction.values())  {
    //do something with dir
}

我想对Rust枚举做类似的事情。


关于Enum特质存在一个问题,该特质可能可以被派发给类似for dir in Direction::values() { ... }(也许)的东西。
休恩2014年

Answers:


40

不,没有。我认为这是因为Rust中的枚举比Java中的枚举要强大得多-它们实际上是成熟的代数数据类型。例如,您希望如何迭代此枚举的值:

enum Option<T> {
    None,
    Some(T)
}

它的第二个成员Some不是静态常量-您可以使用它创建以下值Option<T>

let x = Some(1);
let y = Some("abc");

因此,没有一种明智的方法可以遍历任何枚举的值。

当然,我认为,可以在编译器中添加对静态枚举(即仅带有静态项目的枚举)的特殊支持,因此它将生成一些函数,这些函数返回带有它们的枚举或静态向量的值,但是我相信编译器中额外的复杂性是不值得的。

如果您确实需要此功能,则可以编写自定义语法扩展名(请参阅问题)。此扩展名应接收标识符列表,并输出一个枚举和一个静态常量向量,并将这些标识符作为内容。常规宏也可以在某种程度上起作用,但是据我所知,您不能两次复制具有多重性的宏参数,因此您必须手动编写两次enum元素,这并不方便。

同样,这个问题可能引起一些关注:#5417

当然,您总是可以编写代码来手动返回枚举元素列表。


这是有帮助的。我认为对我来说,最简单的操作可能是编写枚举枚举,以返回值列表或迭代器之类的东西。谢谢!
dougli1sqrd 2014年

42
我谨不同意枚举枚举是不值得的。这是一件常见且有用的事情,因此为什么将它们称为枚举!当然,具有强大的代数能力,但最常见的情况也很方便。
Rex Kerr 2014年

1
@RexKerr,那只是我对为什么此功能仍然不存在的原因的建议。我相信,停止在相应问题上进行讨论可以支持我的观点。顺便说一句,当我需要一次处理所有枚举元素时,通常是某种索引(例如哈希映射),它允许通过某个键来检索枚举值,并且无论如何应该手动构建该索引。
弗拉基米尔·马特维耶夫(Fladimir Matveev)2014年

3
@VladimirMatveev-嗯,应该手动构建的东西很少,可以自动构建。有编译器的意思,不是吗?但是我同意你的分析。
Rex Kerr 2014年

51

您可以使用strum条板箱轻松遍历枚举的值。

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

43

如果枚举类似于C(如您的示例所示),则可以创建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来获得值的迭代器:

impl Direction {
    pub fn iterator() -> impl Iterator<Item = Direction> {
        [North, South, East, West].iter().copied()
    }
}

也可以看看:


实际上,这似乎比使用锈中的“枚举”满足您的需求更好。我实际上想知道,如果打电话给他们的枚举,工会或其他东西,发情是否会更好。另外,作为强大的变体,您可以关联其他信息,例如:[North(0),South(180),EAST(90),WEST(270)]等,或者在给定元组帮助的情况下提供更多信息……
bluejekyll

11

我在板条箱中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)
})

0

如果您不想导入第三方板条箱,则可以创建自己的宏。这是我实现的方式(可能有一些方法可以改善这一点):

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中的简单枚举。


0

您可以使用关联的常量:

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

fn main() {
    for direction in Direction::VALUES.iter().copied() {
        todo!();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.