Menu
Coddy logo textTech

Fonctions génériques

Fait partie de la section Object Oriented Programming du Journey Rust de Coddy — leçon 27 sur 61.

Les génériques ne sont pas limités aux structs — vous pouvez également écrire des fonctions autonomes qui fonctionnent avec n'importe quel type. C'est utile lorsque vous avez besoin d'une fonction utilitaire qui n'appartient pas à une struct spécifique mais qui doit tout de même être flexible.

La syntaxe reflète 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. Le <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 structures :

fn make_pair<T, U>(first: T, second: U) -> (T, U) {
    (first, second)
}

let pair = make_pair(10, "ten");  // renvoie (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, réduisant ainsi la duplication de code tout en maintenant la sécurité des types.

challenge icon

Défi

Facile

Construisons un module utilitaire avec des fonctions génériques qui peuvent fonctionner avec n'importe quel type ! Vous allez créer des fonctions autonomes qui démontrent comment les génériques rendent votre code flexible et réutilisable sans être lié à une structure spécifique.

Vous organiserez votre code sur deux fichiers :

  • utils.rs : Créez une collection de fonctions utilitaires génériques publiques :
    • wrap_in_pair<T> — prend une seule valeur et renvoie un tuple contenant cette valeur deux fois : (value, value). Cela nécessite que le type soit Clone, utilisez 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 : Importez votre module utilitaire et démontrez ces fonctions génériques fonctionnant avec différents types. Montrez comment les mêmes définitions de fonctions gèrent les entiers, les flottants et les chaînes de caractères de manière transparente.

Dans votre fichier principal, démontrez vos fonctions utilitaires en :

  1. Utilisant wrap_in_pair avec un entier (première entrée, analysée comme i32) et en affichant les deux éléments
  2. Utilisant wrap_in_pair avec une chaîne de caractères (deuxième entrée) et en affichant les deux éléments
  3. Utilisant swap avec un entier (troisième entrée, analysée comme i32) et une chaîne de caractères (quatrième entrée), puis en affichant le résultat inversé

Votre sortie doit suivre 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)

Remarquez comment wrap_in_pair fonctionne de manière identique pour les entiers et les chaînes de caractères, et comment swap gère deux types complètement différents — c'est toute la flexibilité des fonctions génériques !

Vous recevrez quatre entrées : un entier, une chaîne de caractères, un autre entier et une autre chaîne de caractères.

Aide-mémoire

Les fonctions génériques vous permettent d'écrire des fonctions utilitaires autonomes qui fonctionnent avec n'importe quel type. Déclarez le paramètre générique entre chevrons après le nom de la fonction :

fn identity<T>(value: T) -> T {
    value
}

Le <T> déclare le paramètre de type générique, qui peut ensuite être utilisé dans les paramètres de fonction et les types de retour. Rust déduit le type concret lorsque vous appelez la fonction :

let num = identity(42);        // T est i32
let text = identity("hello");  // T est &str

Vous pouvez utiliser plusieurs paramètres génériques dans une seule fonction :

fn make_pair<T, U>(first: T, second: U) -> (T, U) {
    (first, second)
}

let pair = make_pair(10, "ten");  // retourne (i32, &str)

Lorsqu'un type générique a besoin de capacités spécifiques, utilisez des bornes de trait (trait bounds) :

fn duplicate<T: Clone>(value: T) -> (T, T) {
    (value.clone(), value)
}

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 inversé
    // Format : Swapped: ({string}, {int})
}
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 Object Oriented Programming