Menu
Coddy logo textTech

Itérer sur des objets de trait

Fait partie de la section Programmation Orientée Objet du Journey Rust de Coddy. Leçon 47 sur 61.

Maintenant que tu peux stocker différents types dans un Vec<Box<dyn Trait>>, l’étape suivante consiste à traiter cette collection. Itérer sur des objets de trait fonctionne exactement comme itérer sur n’importe quel vecteur. Tu parcours chaque élément et appelles les méthodes définies par le trait.

trait Shape {
    fn area(&self) -> f64;
}

struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }

impl Shape for Circle {
    fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 { self.width * self.height }
}

fn main() {
    let shapes: Vec<Box<dyn Shape>> = vec![
        Box::new(Circle { radius: 2.0 }),
        Box::new(Rectangle { width: 3.0, height: 4.0 }),
        Box::new(Circle { radius: 1.0 }),
    ];

    for shape in &shapes {
        println!("Area: {}", shape.area());
    }
}

Lorsque vous effectuez une itération avec for shape in &shapes, chaque shape est une référence vers un Box<dyn Shape>. Vous pouvez appeler directement n’importe quelle méthode du trait Shape : Rust gère automatiquement la répartition dynamique, en appelant l’implémentation correcte pour chaque type concret.

Ce modèle est puissant pour agréger des résultats. Vous pourriez additionner toutes les aires, filtrer les formes par taille ou transformer la collection. L’idée essentielle est qu’une fois les éléments dans une collection d’objets de trait, vous interagissez avec eux uniquement par l’intermédiaire de l’interface du trait, quels que soient leurs types sous-jacents.

challenge icon

Défi

Facile

Construisons un calculateur de prix pour un panier d’achat pouvant contenir différents types d’articles ! Vous allez créer un système dans lequel des produits et des services, chacun avec une logique de tarification différente, peuvent être stockés ensemble et traités afin de calculer un prix total.

Vous organiserez votre code sur deux fichiers :

  • items.rs : définissez un trait public Priceable avec une méthode price qui prend &self et renvoie un f64. Créez ensuite deux structures publiques :
    • Product : avec les champs publics name (String) et cost (f64). Son price doit simplement renvoyer le cost.
    • Service : avec les champs publics name (String), hourly_rate (f64) et hours (f64). Son price doit renvoyer hourly_rate * hours.
  • main.rs : importez votre module d’articles et créez un vecteur de type Vec<Box<dyn Priceable>>. Utilisez les entrées pour créer un Product et un Service, ajoutez-les à votre vecteur, puis parcourez la collection afin de calculer et d’afficher le prix total de tous les articles.

C’est ici que la puissance des objets de trait se révèle. Votre code de parcours n’a pas besoin de savoir s’il traite un Product ou un Service. Il appelle simplement price() sur chaque article, et Rust sélectionne automatiquement l’implémentation correcte.

Votre sortie doit afficher le prix total avec deux décimales :

Total: ${total}

Par exemple, avec les entrées Laptop, 999.99, Consulting, 150.0 et 3.0 :

Total: $1449.99

Et avec les entrées Book, 29.99, Tutoring, 50.0 et 2.0 :

Total: $129.99

Vous recevrez cinq entrées : le nom du produit, le coût du produit, le nom du service, le taux horaire et le nombre d’heures travaillées (analysez les valeurs numériques comme des f64).

Essayez vous-même

mod items;

use items::{Priceable, Product, Service};

fn main() {
    // Lire les entrées
    let mut product_name = String::new();
    std::io::stdin().read_line(&mut product_name).expect("Failed to read line");
    let product_name = product_name.trim().to_string();

    let mut product_cost = String::new();
    std::io::stdin().read_line(&mut product_cost).expect("Failed to read line");
    let product_cost: f64 = product_cost.trim().parse().expect("Failed to parse");

    let mut service_name = String::new();
    std::io::stdin().read_line(&mut service_name).expect("Failed to read line");
    let service_name = service_name.trim().to_string();

    let mut hourly_rate = String::new();
    std::io::stdin().read_line(&mut hourly_rate).expect("Failed to read line");
    let hourly_rate: f64 = hourly_rate.trim().parse().expect("Failed to parse");

    let mut hours = String::new();
    std::io::stdin().read_line(&mut hours).expect("Failed to read line");
    let hours: f64 = hours.trim().parse().expect("Failed to parse");

    // TODO: Créer un Product en utilisant product_name et product_cost

    // TODO: Créer un Service en utilisant service_name, hourly_rate et hours

    // TODO: Créer un Vec<Box<dyn Priceable>> et y ajouter les deux éléments

    // TODO: Parcourir le vecteur et calculer le prix total

    // TODO: Afficher le total avec une décimale
    // Format : Total: ${total:.1}
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet

Entraînez-vous par vous-même : Compilateur Rust en ligne