Phase 3Structures de données

#9 Structs

struct, impl, méthodes

Définir des structs

Une struct (structure) permet de regrouper des données liées sous un même nom. C'est l'outil principal pour créer des types personnalisés en Rust.

Définition d'une struct
struct Rectangle {
    largeur: u32,
    hauteur: u32,
}

Chaque champ a un nom et un type. Par convention, les noms de structs sont en PascalCase et les noms de champs en snake_case.

Créer des instances

Pour créer une instance, on spécifie une valeur pour chaque champ :

Créer une instance
let rect = Rectangle {
    largeur: 30,
    hauteur: 50,
};

println!("Rectangle : {} x {}", rect.largeur, rect.hauteur);

On accède aux champs avec la notation pointée. Rust offre aussi une syntaxe raccourcie quand le nom de la variable correspond au nom du champ :

Syntaxe raccourcie et spread
fn creer_rectangle(largeur: u32, hauteur: u32) -> Rectangle {
    Rectangle {
        largeur,  // Raccourci : équivaut à largeur: largeur
        hauteur,
    }
}

// Créer une struct à partir d'une autre
let rect2 = Rectangle {
    largeur: 10,
    ..rect // Copie les champs restants depuis rect
};

Méthodes avec impl

Les méthodes sont des fonctions attachées à une struct. On les définit dans un bloc impl. Le premier paramètre est toujours &self (ou &mut self pour modifier).

Méthodes
impl Rectangle {
    fn aire(&self) -> u32 {
        self.largeur * self.hauteur
    }

    fn peut_contenir(&self, autre: &Rectangle) -> bool {
        self.largeur > autre.largeur && self.hauteur > autre.hauteur
    }
}

fn main() {
    let rect1 = Rectangle { largeur: 30, hauteur: 50 };
    let rect2 = Rectangle { largeur: 10, hauteur: 40 };

    println!("Aire : {} pixels carrés", rect1.aire());
    println!("Peut contenir rect2 : {}", rect1.peut_contenir(&rect2));
}

&self est un raccourci pour self: &Self, où Self est un alias pour le type de la struct. La méthode emprunte l'instance sans en prendre possession.

Fonctions associées

Les fonctions dans un bloc impl qui ne prennent pas self en paramètre sont des fonctions associées. On les appelle avec ::. Elles servent souvent de constructeurs.

Fonction associée (constructeur)
impl Rectangle {
    fn carre(taille: u32) -> Self {
        Self {
            largeur: taille,
            hauteur: taille,
        }
    }
}

fn main() {
    let c = Rectangle::carre(25);
    println!("Créé un carré de {}x{}", c.largeur, c.hauteur);
}

String::from() que vous avez déjà utilisé est exactement ce type de fonction associée.

Tuple structs

Les tuple structs sont des structs dont les champs n'ont pas de nom. Elles sont utiles pour créer des types distincts à partir de tuples.

Tuple structs
struct Color(u8, u8, u8);
struct Point(f64, f64, f64);

fn main() {
    let orange = Color(255, 128, 0);
    let origine = Point(0.0, 0.0, 0.0);

    println!("Couleur : Color({}, {}, {})", orange.0, orange.1, orange.2);

    // Color et Point sont des types différents,
    // même si leur structure est similaire
}

On accède aux champs par leur index (.0, .1, etc.). L'avantage par rapport à un simple tuple : Color et Point sont des types distincts, même s'ils ont la même structure interne.

À vous de jouer

Essayez les commandes ci-dessous pour travailler avec des structs :

terminal — cargo
user@stemlegacy:~/structs-demo$