web-dev-qa-db-fra.com

Dans Rust, existe-t-il un moyen d'itérer à travers les valeurs d'un enum?

Je viens d'un Java background et je pourrais avoir quelque chose comme enum Direction { NORTH, SOUTH, EAST, WEST} et je pourrais faire quelque chose avec chacune des valeurs tour à tour avec la boucle for améliorée comme:

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

Je voudrais faire une chose similaire avec Rust énumérations.

53
dougli1sqrd

Non, il n'y en a pas. Je pense que c'est parce que les énumérations dans Rust sont beaucoup plus puissantes que dans Java - elles sont en fait à part entière types de données algébriques =. Par exemple, comment vous attendriez-vous à parcourir les valeurs de cette énumération:

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

?

Son deuxième membre, Some, n'est pas une constante statique - vous l'utilisez pour créer des valeurs de Option<T>:

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

Il n'y a donc aucun moyen sain d'itérer sur les valeurs de n'importe quelle énumération.

Bien sûr, je pense qu'il serait possible d'ajouter un support spécial pour les énumérations statiques (c'est-à-dire les énumérations avec uniquement des éléments statiques) dans le compilateur, il serait donc générer une fonction qui renvoie des valeurs de l'énumération ou un vecteur statique avec eux, mais je crois que la complexité supplémentaire dans le compilateur n'en vaut pas la peine.

Si vous voulez vraiment cette fonctionnalité, vous pouvez écrire une extension de syntaxe personnalisée (voir this issue). Cette extension doit recevoir une liste d'identifiants et produire une énumération et un vecteur constant statique avec ces identifiants comme contenu. Une macro régulière fonctionnerait également dans une certaine mesure, mais pour autant que je me souvienne, vous ne pouvez pas transcrire deux fois des arguments de macro avec une multiplicité, vous devrez donc écrire deux fois les éléments enum manuellement, ce qui n'est pas pratique.

Ce problème peut également présenter un certain intérêt: # 5417

Et bien sûr, vous pouvez toujours écrire du code qui retourne une liste d'éléments enum à la main.

25
Vladimir Matveev

Si l'énumération est de type C (comme dans votre exemple), vous pouvez le faire:

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.into_iter()
    }
}


fn main() {
    for dir in Direction::iterator() {
        println!("{:?}", dir);
    }
}
27
A.B.

Vous pouvez maintenant utiliser la caisse Strum pour parcourir facilement les valeurs d'un Enum.

extern crate strum;
#[macro_use] extern crate strum_macros;

use strum::IntoEnumIterator;

#[derive(Display, EnumIter)]
enum Direction
{
    NORTH,
    SOUTH,
    EAST,
    WEST
}

fn main()
{
    for direction in Direction::iter()
    {
        println!("{}", direction);
    }
}

Production:

NORTH
SOUTH
EAST
WEST
9
Jordan Mack

J'ai implémenté des fonctionnalités de base dans la caisse plain_enum .

Il peut être utilisé pour déclarer une énumération de type C comme suit:

#[macro_use]
extern crate plain_enum;

plain_enum_mod!(module_for_enum, EnumName {
    EnumVal1,
    EnumVal2,
    EnumVal3,
});

Et vous permettra alors de faire des choses comme les suivantes:

for value in EnumName::values() {
    // do things with value
}

let enummap = EnumName::map_from_fn(|value| {
    convert_enum_value_to_mapped_value(value)
})
8
phimuemue