Plusieurs types génériques
Fait partie de la section Programmation Orientée Objet du Journey Rust de Coddy. Leçon 26 sur 61.
Parfois, un seul paramètre générique ne suffit pas. Que faire si vous avez besoin d’une structure qui contient deux valeurs, et que ces valeurs peuvent être de types différents ? Rust vous permet d’utiliser plusieurs paramètres génériques en les séparant par des virgules.
Voici une structure Pair qui contient deux valeurs de types potentiellement différents :
struct Pair<T, U> {
first: T,
second: U,
}
Le <T, U> déclare deux paramètres de type indépendants. T et U peuvent être du même type ou de types complètement différents : c’est à vous de décider lors de la création d’une instance :
let both_ints = Pair { first: 10, second: 20 }; // T=i32, U=i32
let mixed = Pair { first: "hello", second: 3.14 }; // T=&str, U=f64
let reversed = Pair { first: 100, second: "world" }; // T=i32, U=&str
Lors de l’implémentation des méthodes, vous déclarez tous les paramètres génériques dans le bloc impl :
impl<T, U> Pair<T, U> {
fn get_first(&self) -> &T {
&self.first
}
fn get_second(&self) -> &U {
&self.second
}
}
Ce modèle est courant lorsque vous devez associer deux éléments de données liés, mais potentiellement différents, comme une paire clé-valeur, des coordonnées avec différents types numériques ou un résultat associé à des métadonnées.
Défi
FacileConstruisons un système de stockage clé-valeur qui démontre la puissance de plusieurs types génériques ! Vous allez créer une structure KeyValue capable d'associer deux types quelconques, idéale pour associer des libellés à des données, des identifiants à des objets ou toute autre combinaison.
Vous allez organiser votre code sur deux fichiers :
keyvalue.rs: définissez une structure générique publique appeléeKeyValue<K, V>avec deux champs privés :keyde typeKetvaluede typeV. Implémentez des méthodes pour utiliser cette structure :- Une fonction associée
newqui crée unKeyValueà partir d'une clé et d'une valeur - Une méthode
keyqui renvoie une référence vers la clé - Une méthode
valuequi renvoie une référence vers la valeur - Une méthode
set_valuequi remplace la valeur actuelle par une nouvelle valeur
- Une fonction associée
main.rs: importez votre module et montrez comment la même définition de structure fonctionne avec différentes combinaisons de types. Vous créerez des paires clé-valeur avec des types mixtes afin de montrer la flexibilité de plusieurs génériques.
Dans votre fichier principal, créez et utilisez les paires clé-valeur suivantes :
- Une paire avec une clé de type chaîne et une valeur entière (la première entrée comme clé, la deuxième entrée analysée en
i32comme valeur) - Affichez la clé et la valeur
- Mettez à jour la valeur avec un nouvel entier (la troisième entrée analysée en
i32) - Affichez la valeur mise à jour
- Créez une deuxième paire avec une clé entière et une valeur de type chaîne (la quatrième entrée analysée en
i32comme clé, la cinquième entrée comme valeur) - Affichez la clé et la valeur de cette paire
Votre sortie doit respecter ce format :
Key: {key}, Value: {value}
Updated value: {value}
Key: {key}, Value: {value}Par exemple, avec les entrées name, 42, 100, 7 et lucky :
Key: name, Value: 42
Updated value: 100
Key: 7, Value: luckyRemarquez que la première paire utilise KeyValue<String, i32>, tandis que la seconde utilise KeyValue<i32, String> : la même structure, mais des combinaisons de types différentes !
Vous recevrez cinq entrées : une clé de type chaîne, une valeur entière, un entier de remplacement, une clé entière et une valeur de type chaîne.
Essayez vous-même
mod keyvalue;
use keyvalue::KeyValue;
fn main() {
// Lire cinq entrées
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let string_key = input1.trim().to_string();
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let int_value: i32 = input2.trim().parse().expect("Failed to parse");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let replacement_int: i32 = input3.trim().parse().expect("Failed to parse");
let mut input4 = String::new();
std::io::stdin().read_line(&mut input4).expect("Failed to read line");
let int_key: i32 = input4.trim().parse().expect("Failed to parse");
let mut input5 = String::new();
std::io::stdin().read_line(&mut input5).expect("Failed to read line");
let string_value = input5.trim().to_string();
// TODO: Créer une paire KeyValue<String, i32> en utilisant string_key et int_value
// TODO: Afficher la clé et la valeur au format : "Key: {key}, Value: {value}"
// TODO: Mettre à jour la valeur en utilisant set_value avec replacement_int
// TODO: Print the updated value in format: "Updated value: {value}"
// TODO: Créer une paire KeyValue<i32, String> en utilisant int_key et string_value
// TODO: Afficher la clé et la valeur de cette paire au format : "Key: {key}, Value: {value}"
}
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
1Méthodes et comportement
Introduction aux blocs d'implémentationLe paramètre SelfMéthodes mutablesFonctions associéesBlocs d'implémentation multiplesChaînage de méthodesRécapitulatif - Actions sur le rectangle4Projet : Animal virtuel
Définir l'animalNourrir l'animal7Traits standards
Le trait DebugLe trait DisplayClone et CopyLes traits d'égalitéRécapitulatif - Point affichable10Projet : Système de documents
Le trait DrawComposant texte2Encapsulation et modules
Bases des modulesLe mot-clé publicChamps privésGettersSettersRécapitulatif - Casier sécurisé5Généricité
Structures génériquesMéthodes génériquesPlusieurs types génériquesFonctions génériquesRécapitulatif - Point de coordonnées8Les Traits comme contraintes
Syntaxe des contraintes de traitContraintes multiplesLa clause whereRetourner des types avec des traitsRécapitulatif - Afficheur générique11Design Patterns en Rust
Le pattern NewtypeCompositionLe trait DropFrom et IntoRécapitulatif - Mock de Smart Pointer3Enums avancées
Enums avec donnéesMéthodes sur les EnumsMatching des variantes de donnéesL'Enum Option revisitéeRécapitulatif - Enum Shape6Définition des Traits
Qu'est-ce qu'un Trait ?Implémenter des TraitsImplémentations par défautRedéfinir les comportements par défautTraits avec paramètresRécapitulatif - Lecteur multimédiaEntraînez-vous par vous-même : Compilateur Rust en ligne