Rust n'a pas de support natif pour la POO, mais vous pouvez quand même utiliser ces techniques pour tirer parti du paradigme.

La programmation orientée objet (POO) simplifie la conception de logiciels en mettant l'accent sur l'utilisation d'objets pour représenter des entités et des concepts du monde réel. La POO encourage la maintenabilité en encapsulant des fonctionnalités dans des objets.

Rust est un langage flexible qui prend en charge la programmation fonctionnelle et procédurale. Bien qu'il ne prenne pas en charge la programmation orientée objet de manière native, vous pouvez implémenter des concepts OOP à l'aide des types de données intégrés de Rust.

Encapsulation dans Rust

L'encapsulation implique l'organisation du code en unités autonomes qui cachent les détails internes tout en exposer une interface publique pour une interaction externe afin de minimiser la complexité et d'améliorer le code maintenabilité.

Vous pouvez encapsuler du code Rust avec des modules. Un module est une collection d'éléments comprenant des fonctions, des structures, des énumérations et des constantes. Les modules Rust fournissent des fonctionnalités pour regrouper et définir les limites entre les parties d'un programme.

instagram viewer

Utilisation de modules pour encapsuler des données et des fonctions

Vous pouvez définir un module à l'aide de la mode mot-clé suivi d'un nom :

mode mon_module {
// les éléments du module vont ici
}

Vous pouvez organiser les modules de manière hiérarchique en imbriquant leurs déclarations :

mode module_parent {
mode mon_module {
// les éléments du module vont ici
}
}

Vous pouvez ensuite faire référence à des modules imbriqués avec la hiérarchie complète, en séparant chaque module par un double deux-points, par exemple, module_parent:: mon_module.

Par défaut, les éléments des modules sont privés et uniquement accessibles au code du même module. Mais vous pouvez rendre les modules publics en utilisant le pub mot-clé:

mode mon_module {
pubfnma_fonction() {
// le corps de la fonction va ici
}
}

Vous pouvez alors accéder ma_fonction d'autres parties de votre programme.

Utiliser des traits pour définir des comportements

Une autre façon dont Rust permet l'encapsulation consiste à utiliser des traits. Les traits définissent les comportements que les types peuvent implémenter et garantissent que différents types sont conformes à la même interface.

pubtraitImprimable {
fnimprimer(&soi);
}

pubstructureMon type {
// champs de structure ici
}

mettre en œuvre Imprimable pour Mon type {
fnimprimer(&soi) {
// implémentation ici
}
}

Le Imprimable trait a un imprimer méthode, et la Mon type struct implémente la Imprimable trait en mettant en œuvre le imprimer méthode.

En utilisant des traits, vous pouvez vous assurer que tout type qui implémente le Imprimable trait a un imprimer méthode. Ceci est pratique lorsque vous travaillez avec du code générique qui doit interagir avec différents types partageant un comportement commun.

Héritage en Rust

L'héritage vous permet de définir une classe basée sur une autre. La sous-classe héritera des propriétés et des méthodes de son parent.

Dans Rust, vous êtes encouragé à utiliser la composition au lieu de l'héritage. La composition est un processus de création de nouveaux objets en combinant ceux qui existent déjà. Au lieu de créer une nouvelle classe qui hérite des fonctionnalités de la classe de base, vous pouvez créer une nouvelle structure contenant une instance de la structure de base et ses champs.

Création de nouveaux types en combinant des types existants

Vous utiliserez des énumérations et des structures pour créer de nouveaux types. Les énumérations sont pratiques pour les types avec des valeurs finies et les structures peuvent contenir plusieurs champs.

Vous pouvez créer un type d'énumération pour différents types d'animaux.

énumérationAnimal {
Chat,
Chien,
Oiseau,
// ...
}

Alternativement, vous pouvez créer une structure contenant des champs pour chaque type d'animal. Les structures peuvent contenir des énumérations et d'autres types.

structureAnimal {
nom: Chaîne,
âge: u8,
animal_type: AnimalType,
}

énumérationType d'animal {
Chat,
Chien,
Oiseau,
// ...
}

Le Animal struct contient les valeurs de la Type d'animal type d'énumération.

Vous pouvez utiliser des traits pour implémenter l'héritage et ajouter un comportement à un type sans en créer un nouveau.

traitVoler {
fnvoler(&soi);
}

Voici comment vous pouvez implémenter le Voler trait pour plusieurs types.

structureOiseau {
nom: Chaîne,
envergure: f32,
}

mettre en œuvre Voler pour Oiseau {
fnvoler(&soi) {
imprimez !("{} vole !", soi.nom);
}
}

structureAvion {
modèle: Chaîne,
vitesse maximale: u32,
}

mettre en œuvre Voler pour Avion {
fnvoler(&soi) {
imprimez !("{} vole !", soi.modèle);
}
}

Le Oiseau et Avion les structures implémentent Voler trait et imprimer des chaînes avec le Imprimez ! macro.

Vous pouvez appeler le voler méthode sur les deux structures sans connaître leurs types spécifiques.

fnprincipal() {
laisser oiseau = oiseau {
nom: Chaîne::depuis("Aigle"),
envergure: 2.0,
};

laisser avion = avion {
modèle: Chaîne::depuis("Boeing 747"),
vitesse maximale: 900,
};

laisser objets_volants: Vecdynamique Voler> = vec![&oiseau, &avion] ;

pour objet dans objets_volants {
objet.fly();
}
}

Le principal fonction instancie le Avion et Oiseau les types. Le objets_volants vector est un vecteur des instances d'objet, et le pour boucle parcourt le vecteur et appelle le voler méthode sur les instances.

Implémentation du polymorphisme dans Rust

Une classe ou un type est polymorphe si plusieurs types représentent une interface. Étant donné que les traits fournissent la fonctionnalité de définition des comportements dans Rust, tout en fournissant une interface commune pour écrire du code générique, vous pouvez utiliser les traits pour implémenter le polymorphisme.

Voici un trait nommé Dessinable qui définit le comportement de rendu des objets à l'écran :

traitDessinable {
fndessiner(&soi);
}

Les types qui implémentent le trait Drawable peuvent accéder au dessiner fonction.

structureRectangle {
largeur: u32,
hauteur: u32,
}

mettre en œuvre Dessinable pour Rectangle {
fndessiner(&soi) {
// Affiche le rectangle à l'écran
}
}

Vous pouvez écrire du code générique qui dessine des objets qui implémentent le Dessinable trait.

fndraw_object(objet: &T) {
objet.dessiner();
}

Le draw_object la fonction prend un type générique J comme entrée qui implémente Dessinable trait et appelle le dessiner méthode sur le trait. Différents objets peuvent implémenter le Dessinable trait et accéder à la fonctionnalité.

Implémentation de l'abstraction dans Rust

L'abstraction est un concept POO où les classes et les interfaces sont accessibles aux objets et types spécifiés. Vous pouvez implémenter l'abstraction dans Rust avec des traits.

Voici un exemple de caractéristique pour un lecteur multimédia :

traitMédias {
fnjouer(&soi);
}

Structures et énumérations qui implémentent Médias trait doit fournir une implémentation pour le jouer méthode.

structureChanson {
titre: Chaîne,
artiste: Chaîne,
}

mettre en œuvre Médias pour Chanson {
fnjouer(&soi) {
imprimez !("Lecture de la chanson: {} par {}", soi.titre, soi.artiste);
}
}

Le Chanson struct implémente la Médias trait en fournissant une implémentation pour le jouer méthode qui imprime un message avec les champs de la Chanson structures à la console.

fnprincipal() {
// Crée une instance de la structure Song
laisser chanson = chanson {
titre: Chaîne::depuis("Rhapsodie bohémienne"),
artiste: Chaîne::depuis("Reine"),
};

// Appel de la méthode de lecture sur l'instance du morceau
chanson.play();
}

Le chanson variable est une instance de Chanson struct, et la variable peut accéder et appeler le jouer méthode.

L'organisation du code de rouille est facile

La programmation orientée objet aide à l'organisation du code. Grâce au système de modules de Rust, vous pouvez facilement organiser votre code Rust tout en implémentant les concepts OOP pour votre application afin de garder votre code organisé, gérable et intuitif.