Fonctions génériques
Fait partie de la section Programmation Orientée Objet du Journey Rust de Coddy. Leçon 27 sur 61.
Les génériques ne se limitent pas aux structures. Vous pouvez également écrire des fonctions autonomes qui fonctionnent avec n’importe quel type. Cela est utile lorsque vous avez besoin d’une fonction utilitaire qui n’appartient pas à une structure spécifique, mais qui doit tout de même rester flexible.
La syntaxe reprend ce que vous avez vu avec les structures. Déclarez le paramètre générique entre chevrons après le nom de la fonction, puis utilisez-le dans les paramètres et le type de retour :
fn identity<T>(value: T) -> T {
value
}
Cette fonction identity prend une valeur de n’importe quel type et la renvoie inchangée. <T> déclare le générique, value: T l’accepte comme paramètre et -> T spécifie le type de retour. Lorsque vous appelez la fonction, Rust déduit le type concret :
let num = identity(42); // T est i32
let text = identity("hello"); // T est &str
Vous pouvez également utiliser plusieurs paramètres génériques dans les fonctions, tout comme avec les structs :
fn make_pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
let pair = make_pair(10, "ten"); // retourne (i32, &str)
Les fonctions génériques vous permettent d’écrire une logique réutilisable une seule fois et de l’appliquer à de nombreux types, ce qui réduit la duplication du code tout en maintenant la sécurité des types.
Défi
FacileConstruisons un module utilitaire avec des fonctions génériques capables de fonctionner avec n’importe quel type ! Tu vas créer des fonctions autonomes qui montrent comment les génériques rendent ton code flexible et réutilisable, sans le lier à une structure spécifique.
Tu vas organiser ton code sur deux fichiers :
utils.rs: Crée une collection de fonctions utilitaires génériques publiques :wrap_in_pair<T>: prend une valeur unique et renvoie un tuple contenant deux fois cette valeur :(value, value). Cela nécessite que le type implémenteClone, utilise donc<T: Clone>swap<T, U>: prend deux valeurs de types potentiellement différents et les renvoie dans l’ordre inverse sous forme de tuple(U, T)
main.rs: Importe ton module utilitaire et montre ces fonctions génériques en fonctionnement avec différents types. Montre comment les mêmes définitions de fonctions traitent facilement les entiers, les nombres flottants et les chaînes.
Dans ton fichier principal, montre tes fonctions utilitaires en :
- Utilisant
wrap_in_pairavec un entier (première entrée, analysée commei32) et en affichant les deux éléments - Utilisant
wrap_in_pairavec une chaîne (deuxième entrée) et en affichant les deux éléments - Utilisant
swapavec un entier (troisième entrée, analysée commei32) et une chaîne (quatrième entrée), puis en affichant le résultat de l’échange
Ta sortie doit respecter ce format :
Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})Par exemple, avec les entrées 5, hello, 42 et world :
Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)Remarque que wrap_in_pair fonctionne de manière identique pour les entiers et les chaînes, et que swap traite deux types complètement différents : c’est toute la flexibilité des fonctions génériques !
Tu recevras quatre entrées : un entier, une chaîne, un autre entier et une autre chaîne.
Essayez vous-même
mod utils;
use std::io;
fn main() {
// Lire les quatre entrées
let mut input1 = String::new();
io::stdin().read_line(&mut input1).expect("Failed to read line");
let num1: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
io::stdin().read_line(&mut input2).expect("Failed to read line");
let str1 = input2.trim().to_string();
let mut input3 = String::new();
io::stdin().read_line(&mut input3).expect("Failed to read line");
let num2: i32 = input3.trim().parse().expect("Invalid integer");
let mut input4 = String::new();
io::stdin().read_line(&mut input4).expect("Failed to read line");
let str2 = input4.trim().to_string();
// TODO: Utiliser wrap_in_pair avec num1 et afficher le résultat
// Format: Pair of ints: ({value}, {value})
// TODO: Utiliser wrap_in_pair avec str1 et afficher le résultat
// Format: Pair of strings: ({value}, {value})
// TODO: Utiliser swap avec num2 et str2, puis afficher le résultat échangé
// Format : Échangé : ({string}, {int})
}
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