Gestion de la portée et de la visibilité avec les modules
Dans cette section, nous allons aborder les modules et les autres outils du système de modules, à savoir les chemins qui nous permettent de nommer les éléments ; l’utilisation du mot-clé use qui importe un chemin dans la portée ; et le mot-clé pub qui rend publics les éléments. Nous verrons aussi le mot-clé as, les paquets externes, et l’opérateur glob.
Aide-mémoire sur les modules
Avant de rentrer dans le vif du sujet des modules et des chemins, voyons un guide de référence rapide concernant le fonctionnement des modules, des chemins d’accès, des mot-clés use et pub dans le compilateur, et la manière dont la plupart des développeurs organisent leur code. Nous aborderons des exemples de chacune de ces règles tout au long de ce chapitre, mais ceci est un bon point de référence pour vous rappeler comment les modules fonctionnement.
- Commencez à partir de la crate racine : au moment de compiler une crate, le compilateur cherche dans le fichier de la crate racine (généralement src/lib.rs pour une crate de bibliothèque et src/main.rs pour une crate binaire) pour trouver le code à compiler.
- Déclaration des modules : dans le fichier de la crate racine, vous pouvez déclarer de nouveaux modules ; par exemple, si vous déclarez un module « jardin » avec
mod jardin;, le compilateur cherchera le code du module dans ces emplacements :“- en ligne, dans la paire d’accolades qui remplacent le point-virgule qui suit
mod jardin; - dans le fichier src/jardin.rs ;
- dans le fichier src/jardin/mod.rs.
- en ligne, dans la paire d’accolades qui remplacent le point-virgule qui suit
- Déclaration de sous-modules : dans tout fichier autre que la crate racine, vous pouvez déclarer des sous-modules. Par exemple, vous pouvez déclarer
mod legumes;dans src/garden.rs. Le compilateur cherchera le code du sous-module dans le répertoire portant le nom du module parent aux emplacements suivants :- en ligne, directement après
mod legumes, à l’intérieur des accolades à la place du point-virgule ; - dans le fichier src/jardin/legumes.rs ;
- dans le fichier src/jardin/legumes/mod.rs.
- en ligne, directement après
- Chemins d’accès au code dans les modules : une fois qu’un module fait partie de votre crate, vous pouvez faire référence au code de ce module à partir de n’importe quel autre endroit dans la même crate, du moment que les règles de visibilité le permettent, en utilisant le chemin d’accès au code. Par exemple, un type
Aspergedans le module jardin legumes se trouverait à l’adressecrate::jardin::legumes::Asperge. - Privé ou public : le code situé dans un module est par défaut privé vis-à-vis de ses modules parents. Pour rendre un module public, déclarez-le avec
pub modau lieu demod. Pour rendre publics les éléments d’un module public , utilisezpubavant leur déclaration. - Le mot-clé
use: à l’intérieur d’une portée, le mot-cléusecrée des raccourcis vers des éléments afin de réduire la répétition de longs chemins d’accès. Dans toute portée qui peut faire référence àcrate::jardin::legumes::Asperge, vous pouvez créer un raccourci avecuse crate::jardin::legumes::Aspergepour utiliser ce type dans cette portée.
Ici, nous allons créer une crate binaire nommée cour qui illustre ces règles. Le répertoire de la crate, aussi nommé cour, contient les fichiers et répertoires suivants :
cour
├── Cargo.lock
├── Cargo.toml
└── src
├── jardin
│ └── vegetables.rs
├── jardin.rs
└── main.rs
Le fichier de la crate racine est dans ce cas src/main.rs, et il contient :
use crate::jardin::legumes::Asperge;
pub mod jardin;
fn main() {
let plante = Asperge {};
println!("Je fais pousser {plante:?}!");
}
La ligne pub mod jardin; dit au compilateur d’inclure le code qu’il trouve dans src/jardin.rs, lequel est :
pub mod legumes;
Ici, pub mod legumes; signifie le code dans src/jardin/legumes.rs est également inclus. Ce code est :
#[derive(Debug)]
pub struct Asperge {}
Voyons maintenant en détail ces règles et voyons leur application dans la pratique !
Regroupement de codes associés dans les modules
Les modules nous permettent d’organiser le code d’une crate, pour une meilleure lisibilité et pour la facilité de réutilisation. Les modules nous permettent aussi de gérer la visibilité des éléments car le code dans un module est privé par défaut. Les éléments privés sont des détails d’implémentations internes qui ne sont pas accessibles à l’extérieur. Nous pouvons choisir de rendre publics les modules ainsi que les éléments qu’ils contiennent, ce qui les expose afin de permettre à du code externe de les utiliser et d’en dépendre.
Voici un exemple : écrivons une crate de bibliothèque qui permet de simuler un restaurant. Nous allons définir les signatures des fonctions mais nous allons laisser leurs corps vides pour nous concentrer sur l’organisation du code, plutôt que de coder pour de vrai un restaurant.
Dans le secteur de la restauration, certaines parties d’un restaurant sont assimilées à la salle à manger et d’autres aux cuisines. La partie salle à manger est l’endroit où se trouvent les clients ; c’est l’endroit où les hôtes installent les clients, où les serveurs prennent les commandes et encaissent les clients, et où les barmans préparent des boissons. Dans la partie cuisines, nous retrouvons les chefs et les cuisiniers qui travaillent dans la cuisine, mais aussi les plongeurs qui nettoient la vaisselle et les gestionnaires qui s’occupent des tâches administratives.
Pour organiser notre crate de cette manière, nous pouvons organiser les fonctions avec des modules imbriqués. Créez une nouvelle bibliothèque qui s’appelle restaurant en utilisant cargo new restaurant --lib. Puis écrivez le code de l’encart 7-1 dans src/lib.rs afin de définir quelques modules et quelques signatures de fonctions ; ce code constitue la partie de la salle à manger.
mod salle_a_manger {
mod accueil {
fn ajouter_a_la_liste_attente() {}
fn installer_a_une_table() {}
}
mod service {
fn prendre_commande() {}
fn servir_commande() {}
fn encaisser() {}
}
}
front_of_house module containing other modules that then contain functionsNous définissons un module avec le mot-clé mod suivi par le nom du module (dans notre cas, salle_a_manger). Le corps du module est ensuite placé à l’intérieur d’accolades. Dans les modules, nous pouvons avoir d’autres modules, comme dans notre cas avec les modules accueil et service. Les modules peuvent aussi contenir des définitions pour d’autres éléments, comme des structures, des énumérations, des constantes, des traits, ou des fonctions (comme c’est le cas dans l’encart 7-1).
Grâce aux modules, nous pouvons regrouper ensemble des définitions qui sont liées et donner un nom à ce lien. Les développeurs qui utiliseront ce code pourront parcourir le code en fonction des groupes plutôt que d’avoir à lire toutes les définitions, ce qui permet de trouver plus facilement les définitions dont ils ont besoin. Les développeurs qui veulent rajouter des nouvelles fonctionnalités à ce code sauront maintenant où placer le code tout en gardant le programme organisé.
Précédemment, nous avons dit que src/main.rs et src/lib.rs étaient des crates racines. Nous les appelons ainsi car le contenu de chacun de ces deux fichiers constitue un module qui s’appelle crate à la racine de l’arborescence du module.
L’encart 7-2 présente l’arborescence du module pour la structure de l’encart 7-1.
crate
└── salle_a_manger
├── accueil
│ ├── ajouter_a_la_liste_attente
│ └── installer_a_une_table
└── service
├── prendre_commande
├── servir_commande
└── encaisser
Cette arborescence montre comment les modules sont imbriqués entre eux ; par exemple, accueil est imbriqué dans salle_a_manger. L’arborescence montre aussi que certains modules sont les frères d’autres modules, ce qui veut dire qu’ils sont définis dans le même module ; accueil et service sont définis dans salle_a_manger. Pour prolonger la métaphore familiale, si le module A est contenu dans le module B, on dit que le module A est l’enfant du module B et que ce module B est le parent du module A. Notez aussi que le module implicite nommé crate est le parent de toute cette arborescence.
L’arborescence des modules peut rappeler les répertoires du système de fichiers de votre ordinateur ; et c’est une excellente comparaison ! Comme les répertoires dans un système de fichiers, vous utilisez les modules pour organiser votre code. Et comme pour les fichiers dans un répertoire, nous avons besoin d’un moyen de trouver nos modules.