Définir des modules pour gérer la portée et la visibilité

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. Pour commencer, penchons-nous sur les modules !

Les modules nous permettent de regrouper le code d'une crate pour une meilleure lisibilité et pour la facilité de réutilisation. Les modules permettent aussi de gérer la visibilité des éléments, qui précise si un élément peut être utilisé à l'extérieur du module (c'est public) ou s'il est un constituant interne et n'est pas disponible pour une utilisation externe (c'est privé).

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 la même manière qu'un vrai restaurant, nous pouvons organiser les fonctions avec des modules imbriqués. Créez une nouvelle bibliothèque qui s'appelle restaurant en utilisant cargo new --lib restaurant ; puis écrivez le code de l'encart 7-1 dans src/lib.rs afin de définir quelques modules et quelques signatures de fonctions.

Fichier : src/lib.rs

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() {}
    }
}

Encart 7-1 : Un module salle_a_manger qui contient d'autres modules qui contiennent eux-mêmes des fonctions

Nous définissons un module en commençant avec le mot-clé mod et nous précisons ensuite le nom du module (dans notre cas, salle_a_manger) et nous ajoutons des accolades autour du corps du module. 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 plus facilement trouver les définitions dont ils ont besoin car ils peuvent parcourir le code en fonction des groupes plutôt que d'avoir à lire toutes les définitions. 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 racines de crates. Nous les appelons ainsi car le contenu de chacun de ces deux fichiers constituent 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

Encart 7-2 : L'arborescence des modules pour le code de l'encart 7-1

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 dossiers du système de fichiers de votre ordinateur ; et c'est une excellente comparaison ! Comme les dossiers dans un système de fichiers, vous utilisez les modules pour organiser votre code. Et comme pour les fichiers dans un dossier, nous avons besoin d'un moyen de trouver nos modules.