3. Structs, enums et pattern matching

Pour modéliser des données complexes en Rust, vous disposez de deux outils puissants : les structs (structures) et les enums. Combinés au pattern matching, ils permettent d’écrire du code expressif, exhaustif et sans cas non gérés. Dans cette unité, vous verrez comment définir vos propres types de données et les manipuler de façon idiomatique.

 

Les structs : regrouper des données

Une struct est un type composite qui regroupe plusieurs champs nommés sous un même type. C’est l’équivalent des objets dans d’autres langages, mais sans héritage.

struct Reference {
    identifiant: String,
    prefixe: String,
    numero: u32,
    annee: u16,
}

fn main() {
    let r = Reference {
        identifiant: String::from("DOC-2024-001"),
        prefixe: String::from("DOC"),
        numero: 1,
        annee: 2024,
    };
    println!("Ref : {} ({})", r.identifiant, r.annee);
}

Méthodes avec impl

On ajoute des méthodes à une struct avec un bloc impl. Le premier paramètre &self (ou &mut self) représente l’instance courante.

impl Reference {
    // Méthode associée (constructeur)
    fn new(prefixe: &str, numero: u32, annee: u16) -> Self {
        Reference {
            identifiant: format!("{}-{}-{:03}", prefixe, annee, numero),
            prefixe: prefixe.to_string(),
            numero,
            annee,
        }
    }

    // Méthode d'instance
    fn formater(&self) -> String {
        format!("[{}] #{:04}/{}", self.prefixe, self.numero, self.annee)
    }
}

fn main() {
    let r = Reference::new("DOC", 42, 2024);
    println!("{}", r.formater());  // "[DOC] #0042/2024"
}

 

Les enums : des types à variantes

Les enums Rust sont bien plus puissantes que dans la plupart des langages : chaque variante peut contenir des données de types différents.

enum FormatRef {
    Court,                      // pas de données
    Long(String),               // contient une String
    Numerique { annee: u16, num: u32 }, // champs nommés
}

fn afficher(format: &FormatRef) -> String {
    match format {
        FormatRef::Court => "format court".to_string(),
        FormatRef::Long(s) => format!("long: {}", s),
        FormatRef::Numerique { annee, num } => format!("{}/{}", annee, num),
    }
}

 

Pattern matching avec match

L’expression match est l’outil fondamental pour travailler avec les enums. Le compilateur vérifie que tous les cas sont couverts (exhaustivité).

let score = 85;
let mention = match score {
    90..=100 => "Excellent",
    75..=89  => "Bien",
    60..=74  => "Assez bien",
    _        => "Insuffisant",  // cas par défaut
};
println!("Mention : {}", mention);

if let pour un seul cas

Quand vous ne vous intéressez qu’à un seul variant, if let est plus concis qu’un match complet :

let maybe_ref: Option<String> = Some("DOC-2024-001".to_string());

if let Some(ref_doc) = maybe_ref {
    println!("Référence trouvée : {}", ref_doc);
} else {
    println!("Aucune référence");
}

 

Derive : dériver des traits automatiquement

L’attribut #[derive] génère automatiquement des implémentations de traits courants :

#[derive(Debug, Clone, PartialEq)]
struct Reference {
    identifiant: String,
    numero: u32,
}

fn main() {
    let r = Reference { identifiant: "DOC-001".to_string(), numero: 1 };
    println!("{:?}", r);  // Debug : affiche la struct formatée
    let r2 = r.clone();   // Clone : copie la valeur
}
Points clés :

  • Les structs groupent des champs nommés ; les méthodes s’ajoutent dans un bloc impl
  • Les enums Rust peuvent contenir des données dans chaque variante
  • match est exhaustif : le compilateur force la gestion de tous les cas
  • #[derive(Debug)] permet d’afficher une struct avec {:?}
SEE ALL Add a note
YOU
Add your Comment