Acheter version papier

Rust

Fork me on GitHub

I. Les bases de la programmation en Rust

9. Les enums

Les "enums" sont très différentes de celles que vous pourriez croiser dans des langages impératifs comme le C ou le C++. Elles ne représentent pas juste des nombres mais bien plus :

Runenum UneEnum {
    Variant,
    VariantStruct { a: i32, b: i32 },
    VariantTuple(String),
}

Chaque "champ" d'une enum est appelé un variant. Comme vous avez pu le voir au-dessus, les enums permettent beaucoup plus. Il est cependant aussi possible de déclarer et utiliser des enums plus proches de celles que vous pourriez trouver en C/C++ :

Runenum UneEnum {
    Variant = 12,
    Variant2,
}

UneEnum::Variant vaudra donc 12, par-contre UneEnum::Variant2 ne vaudra pas 13 ! Il vous faudra donner une valeur à chaque variant si vous voulez que ce soit le cas.

Utilisation

Les enums peuvent se révéler très utiles dans beaucoup de cas. Par-exemple, vous avez codé un jeu-vidéo qui fonctionne au tour par tour (un jeu d'échecs ?). Pendant son tour, le joueur peut bouger une pièce ou bien ne rien faire. On peut exprimer ça de la façon suivante :

Runenum Action {
    Bouger { piece: Piece, nouvelle_position: Position },
    Passer,
}

Si le joueur bouge une pièce, on aura Action::Bouger sinon on aura Action::Passer.

Un autre exemple avec du "parsing" d'adresse IP :

Runenum IPKind {
    IPV4(u8, u8, u8, u8),
    IPV6(u32, u32, u32, u32),
}

Et cette enum s'utiliserait comme ceci :

Runfn parse_ip(ip: &str) -> IPKind {
    // "le parsing"
}

match parse_ip("127.0.0.1") {
    IPKind::IPV4(_, _, _, _) => {
        println!("c'est une IPv4 !");
    }
    IPKind::IPV6(_, _, _, _) => {
        println!("c'est une IPv6 !");
    }
}

On peut aussi se servir d'une enum pour gérer des erreurs comme le permettent les enums Result et Option :

Runmatch File::open("fichier.txt") {
    Ok(fichier) => {
        // le fichier existe et on a les droits d'accès, on peut s'en servir !
    }
    Err(e) => {
        // le fichier n'existe pas ou on n'a pas les droits d'accès, on ne
        // peut rien faire...
        println!("Erreur en tentant d'ouvrir le fichier : {:?}", e);
    }
}

Ce sont 2 utilisations très différentes des enums mais qui sont des éléments très importants permettant à Rust d'empêcher ses utilisateurs d'utiliser des types invalides (comme déréférencer un pointeur nul).

Implémenter des méthodes sur une enum

Tout comme pour les structures (que nous verrons dans le chapitre suivant), il est possible d'implémenter des méthodes (et des traits !) sur des enums. Je vais donner un exemple rapide ici mais j'en parle plus en détails dans le chapitre suivant :

Run// On reprend notre enum précédente :
enum IPKind {
    IPV4(u8, u8, u8, u8),
    IPV6(u32, u32, u32, u32),
}

impl IPKind {
    fn is_v4(&self) -> bool {
        match *self {
            IPKind::IPV4(_, _, _, _) => true,
            _ => false,
        }
    }

    fn is_v6(&self) -> bool {
        !self.is_v4() // je suis un peu fainéant :)
    }
}

Je ne vais pas m'étendre plus sur le sujet et vous invite donc à passer au chapitre suivant pour en savoir plus !