English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Rust 枚举类

Les classes d'enumération en Rust ne sont pas aussi simples que dans d'autres langages de programmation, mais elles peuvent tout de même être utilisées très simplement :

#[derive(Debug)]

enum Book {
    Papery, Électronique
}

fn main() {
    let book = Book::Papery;
    println!("{:?}", book);
}

Résultat de l'exécution :

Papery

Les livres sont divisés en livres papiers (livre Papery) et livres électroniques (livre Électronique).

Si vous développez actuellement un système de gestion de bibliothèque, vous devez décrire les différentes propriétés des deux types de livres (les livres papiers ont un numéro de bibliothèque, et les livres électroniques n'ont que l'URL), vous pouvez ajouter des attributs de tuple aux membres de l'enumération :

enum Book {
    Papery(u32),
    Électronique(String),
}
let book = Book::Papery(1001);
let ebook = Book::Électronique(String::from("url://..."));

Si vous souhaitez nommer une propriété, vous pouvez utiliser la syntaxe de structure :

enum Book {
    Papery { index: u32 },
    Électronique { url: String },
}
let book = Book::Papery{index: 1001};

Bien que vous puissiez nommer ainsi, il ne faut pas oublier que vous ne pouvez pas accéder aux attributs liés à l'ensemble ennumérée comme vous le feriez pour les champs d'une structure. La méthode d'accés se trouve dans la syntaxe match.

Syntaxe de match

L'objectif des ensembles ennumérés est de classer un certain type de choses, et l'objectif de la classification est de décrire différentes situations. Sur cette base, les classes ennumérées seront souvent traitées par une structure de branche (comme switch dans de nombreux langages). La syntaxe de switch est classique, mais elle n'est pas prise en charge dans Rust. De nombreux langages ont abandonné switch en raison des problèmes de séquence de connexion qui peuvent survenir en oubliant d'ajouter break, Java et C# utilisent des vérifications de sécurité pour éviter ce type de situation.

Le Rust met en ôuvre une structure de branche par le biais de l'instruction match. Commençons par comprendre comment utiliser match pour traiter les classes ennumérées :

fn main() {
    enum Book {
        Papery{index: u32},
        Electronic{url: String},
    }
   
    let book = Book::Papery{index: 1001};
    let ebook = Book::Electronic{url: String::from( "url...")};
   
    match book{
        Book::Papery{ index} => {
            println!( "Livre en papier {}", index);
        },
        Book::Electronic{url} => {
            println!( "E-book {}", url);
        }
    }
}

Résultat de l'exécution :

Livre en papier 1001

Un bloc match peut être traité comme une expression de fonction, et il peut être accompagné de valeurs de retour :

match exemple d'ensemble ennumérée {
    Classification1 => expression de retour,
    Classification2 => expression de retour,
    ...
}

Mais tous les types d'expressions de retour doivent être identiques !

Si vous définissez des attributs annexes d'une classe ennumérée sous forme de tuple, vous devez spécifier temporairement un nom dans le bloc match :

enum Book {
    Papery(u32),
    Electronic{url: String},
}
let book = Book::Papery(1001);

match book{
    Book::Paperyi) => {
        println!( "{"41;;
    },
    Book::Electronic{url} => {
        println!( "{"41;;
    }
}

En plus de pouvoir effectuer des choix de branche pour les classes ennumérées, match peut également effectuer des choix de branche sur des types de données entiers, flottants, caractères et tronçons de chaîne de caractères (&str). Bien que le type de données flottantes puisse être validement sôlu dans les choix de branche, il n'est pas recommandé de le faire ainsi, car des problèmes de précision peuvent entraêrner des erreurs de branche.

Lorsque vous effectuez un choix de branche pour une classe non ennumérée, vous devez veiller à traiter les cas d'exception, même si rien n'est à faire dans ces cas. Les cas d'exception sont indiqués par une underscore _ :

fn main() {
    let t = "abc";
    match t {
        "abc" => println!("Yes"),
        _ => {},
    }
}

Classe d'enumeration Option

Option est une classe d'enumeration de la bibliothèque standard Rust, cette classe est utilisée pour combler le vide de Rust ne supportant pas les références null.

De nombreux langages supportent l'existence de null (C/C++、Java),c'est très pratique, mais cela crée aussi de graves problèmes, l'inventeur de null admet cela, "une idée pratique a causé des problèmes cumulés 10 de pertes de milliards de dollars".

null est souvent la cause d'une attaque mortelle pour le programmeur : après tout, une seule erreur de ce genre peut entraîner la cessation complète du fonctionnement du programme.

Pour résoudre ce problème, de nombreux langages ne permettent pas par défaut null, mais le support null en niveau de langage (souvent marqué par un symbole ? devant le type).

Java prend en charge par défaut null, mais il est possible de limiter l'apparition de null via l'annotation @NotNull, ce qui est une solution de contournement.

Rust interdit complètement l'existence de null en niveau de langage, mais malheureusement null peut résoudre efficacement un petit nombre de problèmes, donc Rust a introduit la classe d'enumeration Option :

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

Si vous voulez définir une classe qui peut être vide, vous pouvez le faire ainsi :

let opt = Option::Some("Hello");

Si vous voulez exécuter certaines opérations sur opt, vous devez d'abord juger s'il est Option::None:

fn main() {
    let opt = Option::Some40;"Hello");
    match opt123;
        Option::Some40;quelque chose) => {
            println!("{}", quelque chose);
        },
        Option::None => {
            println!("opt n'est rien");
        }
    }
}

Résultat de l'exécution :

Hello

Si votre variable commence par une valeur vide, faites une pensée à l'éditeur de compilation, comment pourrait-il savoir le type de la variable lorsque la valeur n'est pas vide ?

Donc, pour une Option initialement vide, il faut préciser le type explicitement :

fn main() {
    let opt: Option<&str> = Option::None;
    match opt123;
        Option::Some40;quelque chose) => {
            println!("{}", quelque chose);
        },
        Option::None => {
            println!("opt n'est rien");
        }
    }
}

Résultat de l'exécution :

opt n'est rien

Cette conception rend la programmation de valeur vide difficile, mais c'est exactement ce dont a besoin pour construire un système stable et efficace. Étant donné que Option est introduit par défaut par le compilateur Rust, il est possible de l'écrire directement None ou Some() sans l'option Option::.

Option est une classe d'enumeration spéciale qui peut contenir des branches de valeurs de choix :

fn main() {
        let t = Some(64);
        match t {
                Some(64) => println!("Yes"),
                _ => println!("No"),
        }
}

if let 语法

let i = 0;
match i {
    0 => println!("zero"),
    _ => {},
}

放入主函数运行结果:

zero

这段程序的目的是判断 i 是否是数字 0,如果是就打印 zero。

现在用 if let 语法缩短这段代码:

let i = 0;
if let 0 = i {
    println!("zero");
}

if let 语法格式如下:

if let 匹配值 = 源变量 {
    语句块
}

可以在之后添加一个 else 块来处理例外情况。

if let 语法可以认为是只区分两种情况的 match 语句的"语法糖"(语法糖指的是某种语法的原理相同的便捷替代品)。

对于枚举类依然适用:

fn main() {
    enum Book {
        Papery(u32),
        Electronic(String)
    }
    let book = Book::Electronic(String::from("url"));
    if let Book::Papery(index) = book {
        println!("Papery {}", index);
    } else {
        println!("Not papery book");
    }
}