Il y a toujours des niveaux d’incertitude lors du développement d’applications, ce qui peut conduire à des erreurs, en particulier si vos fonctions acceptent des types d’arguments spécifiques. Pour réduire les erreurs dues aux incertitudes, vous pouvez utiliser les types génériques. Les génériques permettent de créer des classes, des fonctions et des structures de données pour travailler avec différents types.
Grâce aux génériques, vous pouvez créer et définir des algorithmes et des structures de données qui peuvent opérer sur plusieurs types sans avoir à écrire un code complexe et des implémentations distinctes pour chaque type. Les génériques améliorent la réutilisation du code et l’efficacité tout en maintenant la sécurité des types et les performances.
Utilisation des types génériques en Rust
Les types génériques de Rust peuvent interagir avec d’autres types de données Rust. Vous définirez les types génériques avec des crochets (<> ;), suivis de deux paramètres ou plus.
Voici une définition de structure générique qui prend deux paramètres de type générique :
struct Point<T, U> {
// T and U are generic type parameters that the x and y fields will
// assume on instantiation
x: T,
y: U,
}
Dans l’élément Point struct, T, et U sont des paramètres de type générique.
Vous pouvez remplacer les paramètres de type générique par n’importe quel type de données lors de l’instanciation :
fn main() {
let my_point = Point { x: String::from("hello"), y: String::from("world") };
println!(
"The x value of my_point is {} and the y value is {}.",
my_point.x,
my_point.y
);
}
L’option mon_point est une instance de la variable Point structure initialisée avec des types de chaînes de caractères. Le compilateur Rust déduit les types concrets de T et U sur la base des valeurs à l’instanciation.
Limites des traits pour les types génériques
Les types génériques Rust peuvent utiliser les limites des traits pour assurer la sécurité des types. Les traits sont des collections de méthodes que les types peuvent implémenter pour afficher certains comportements définis pour le trait.
Les bornes de traits spécifient qu’un type générique doit implémenter un ou plusieurs traits.
Voici un exemple de fonction générique qui renvoie la plus grande de deux valeurs avec une borne de trait qui garantit que les types comparés implémentent le trait :
// Maximum is a trait that defines a method for evaluating the maximum of two
// types
trait Maximum {
fn max(self, other: Self) -> Self;
}
// Implements the `Maximum` trait for all types that implement the
// `PartialOrd` trait.
impl<T: PartialOrd> Maximum for T {
fn max(self, other: Self) -> Self {
// return `self` if it is greater than `other`; otherwise, return
// `other.`
if self > other {
self
} else {
other
}
}
}
fn main() {
let a = 5;
let b = 10;
let largest = Maximum::max(a, b);
println!("The largest value is {}", largest);
}
La fonction Maximum Le trait a un max qui renvoie la plus grande de deux valeurs du même type. Tout type qui implémente la méthode PartialOrd met en œuvre le trait Maximum trait.
Le max prend deux valeurs de la méthode Self type – se référant au type mettant en œuvre le Maximum trait-et compare les valeurs.
Le principal compare deux variables à l’aide de la fonction max et imprime la plus grande.
Contraintes pour les types génériques
Les contraintes sont similaires aux limites des traits, mais elles vous permettent de spécifier des exigences supplémentaires sur les types que vous utilisez comme paramètres de type.
Si vous souhaitez créer une fonction générique qui accepte des types pour la conversion de chaînes de caractères, vous pouvez utiliser une contrainte pour vous assurer que le paramètre de type implémente un trait.
// ToString is a trait with a string conversion method
trait ToString {
fn to_string(&self) -> String;
}
// to_string is a generic function that takes a value of any type that
// implements the ToString trait
fn to_string<T: ToString>(value: T) -> String {
value.to_string()
}
La fonction to_string Le paramètre de valeur doit implémenter la fonction ToString qui garantit que vous pouvez convertir des valeurs de type T à la corde avec le to_string méthode.
Les types génériques sont utiles pour travailler avec les traits
Les types génériques de Rust sont puissants, et il y a des domaines à améliorer. Un domaine critique est l’amélioration de la performance du code générique. Actuellement, le système de types de Rust peut imposer une surcharge au code générique, ce qui ralentit les performances.
Les types génériques sont utiles pour travailler avec les traits. En utilisant les types génériques, vous pouvez créer des objets traits qui fonctionnent avec n’importe quel type implémentant un trait pour rendre vos méthodes plus flexibles.
