Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contrôler la portée et la visibilité avec les modules

Dans cette section, nous parlerons des modules et d’autres parties du système de modules, à savoir les chemins (paths), qui vous permettent de nommer des éléments ; le mot-clé use qui amène un chemin dans la portée ; et le mot-clé pub pour rendre des éléments publics. Nous aborderons également le mot-clé as, les packages externes et l’opérateur glob.

Aide-mémoire des modules

Avant d’entrer dans les détails des modules et des chemins, voici une référence rapide sur le fonctionnement des modules, des chemins, du mot-clé use et du mot-clé pub dans le compilateur, et sur la façon dont la plupart des développeurs organisent leur code. Nous passerons en revue des exemples pour chacune de ces règles tout au long de ce chapitre, mais c’est un excellent endroit auquel se référer pour se rappeler le fonctionnement des modules.

  • Commencer par la racine de la crate : lors de la compilation d’une crate, le compilateur cherche d’abord dans le fichier racine de la crate (généralement src/lib.rs pour une crate de bibliothèque et src/main.rs pour une crate binaire) le code à compiler.
  • Déclarer des modules : dans le fichier racine de la crate, vous pouvez déclarer de nouveaux modules ; par exemple, vous déclarez un module « garden » avec mod garden;. Le compilateur cherchera le code du module aux endroits suivants :
    • En ligne, entre des accolades qui remplacent le point-virgule après mod garden
    • Dans le fichier src/garden.rs
    • Dans le fichier src/garden/mod.rs
  • Déclarer des sous-modules : dans tout fichier autre que la racine de la crate, vous pouvez déclarer des sous-modules. Par exemple, vous pourriez déclarer mod vegetables; dans src/garden.rs. Le compilateur cherchera le code du sous-module dans le répertoire portant le nom du module parent, aux endroits suivants :
    • En ligne, directement après mod vegetables, entre des accolades au lieu du point-virgule
    • Dans le fichier src/garden/vegetables.rs
    • Dans le fichier src/garden/vegetables/mod.rs
  • Chemins vers le 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 depuis n’importe quel autre endroit de la même crate, tant que les règles de confidentialité le permettent, en utilisant le chemin vers le code. Par exemple, un type Asparagus dans le module garden vegetables se trouverait à crate::garden::vegetables::Asparagus.
  • Privé vs. public : le code au sein d’un module est privé par rapport à ses modules parents par défaut. Pour rendre un module public, déclarez-le avec pub mod au lieu de mod. Pour rendre également publics les éléments au sein d’un module public, utilisez pub avant leurs déclarations.
  • Le mot-clé use : au sein d’une portée, le mot-clé use crée des raccourcis vers des éléments pour réduire la répétition de chemins longs. Dans toute portée pouvant faire référence à crate::garden::vegetables::Asparagus, vous pouvez créer un raccourci avec use crate::garden::vegetables::Asparagus;, et à partir de là, il vous suffit d’écrire Asparagus pour utiliser ce type dans la portée.

Ici, nous créons une crate binaire nommée backyard qui illustre ces règles. Le répertoire de la crate, également nommé backyard, contient ces fichiers et répertoires :

backyard
├── Cargo.lock
├── Cargo.toml
└── src
    ├── garden
    │   └── vegetables.rs
    ├── garden.rs
    └── main.rs

Le fichier racine de la crate dans ce cas est src/main.rs, et il contient :

Filename: src/main.rs
use crate::garden::vegetables::Asparagus;

pub mod garden;

fn main() {
    let plant = Asparagus {};
    println!("I'm growing {plant:?}!");
}

La ligne pub mod garden; indique au compilateur d’inclure le code qu’il trouve dans src/garden.rs, qui est :

Filename: src/garden.rs
pub mod vegetables;

Ici, pub mod vegetables; signifie que le code dans src/garden/vegetables.rs est également inclus. Ce code est : rust,noplayground,ignore {{#rustdoc_include ../listings/ch07-managing-growing-projects/quick-reference-example/src/garden/vegetables.rs}}

#[derive(Debug)]
pub struct Asparagus {}

Maintenant, entrons dans les détails de ces règles et voyons-les en action !

Regrouper le code associé dans des modules

Les modules nous permettent d’organiser le code au sein d’une crate pour la lisibilité et la facilité de réutilisation. Les modules nous permettent également de contrôler la confidentialité des éléments, car le code au sein d’un module est privé par défaut. Les éléments privés sont des détails d’implémentation internes non disponibles pour une utilisation externe. Nous pouvons choisir de rendre les modules et les éléments qu’ils contiennent publics, ce qui les expose pour permettre au code externe de les utiliser et d’en dépendre.

À titre d’exemple, écrivons une crate de bibliothèque qui fournit les fonctionnalités d’un restaurant. Nous définirons les signatures des fonctions mais laisserons leurs corps vides pour nous concentrer sur l’organisation du code plutôt que sur l’implémentation d’un restaurant.

Dans le secteur de la restauration, certaines parties d’un restaurant sont désignées comme la salle (front of house) et d’autres comme les cuisines (back of house). La salle est l’endroit où se trouvent les clients ; cela englobe l’endroit où les hôtes placent les clients, où les serveurs prennent les commandes et les paiements, et où les barmans préparent les boissons. Les cuisines sont l’endroit où les chefs et les cuisiniers travaillent, où les plongeurs nettoient et où les responsables effectuent le travail administratif.

Pour structurer notre crate de cette façon, nous pouvons organiser ses fonctions en modules imbriqués. Créez une nouvelle bibliothèque nommée restaurant en exécutant cargo new restaurant --lib. Ensuite, entrez le code du listing 7-1 dans src/lib.rs pour définir quelques modules et signatures de fonctions ; ce code est la section de la salle.

Filename: src/lib.rs
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}
Listing 7-1: A front_of_house module containing other modules that then contain functions

Nous définissons un module avec le mot-clé mod suivi du nom du module (dans ce cas, front_of_house). Le corps du module se place ensuite entre des accolades. À l’intérieur des modules, nous pouvons placer d’autres modules, comme dans ce cas avec les modules hosting et serving. Les modules peuvent également contenir des définitions pour d’autres éléments, tels que des structs, des enums, des constantes, des traits et, comme dans le listing 7-1, des fonctions.

En utilisant des modules, nous pouvons regrouper les définitions associées ensemble et nommer pourquoi elles sont liées. Les développeurs utilisant ce code peuvent naviguer dans le code en se basant sur les groupes plutôt que de devoir lire toutes les définitions, ce qui facilite la recherche des définitions qui les intéressent. Les développeurs ajoutant de nouvelles fonctionnalités à ce code sauraient où placer le code pour garder le programme organisé.

Plus tôt, nous avons mentionné que src/main.rs et src/lib.rs sont appelés racines de crate. La raison de ce nom est que le contenu de l’un où l’autre de ces deux fichiers forme un module nommé crate à la racine de la structure de modules de la crate, connue sous le nom d’arbre de modules.

Le listing 7-2 montre l’arbre de modules pour la structure du listing 7-1.

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment
Listing 7-2: The module tree for the code in Listing 7-1

Cet arbre montre comment certains modules s’imbriquent dans d’autres modules ; par exemple, hosting s’imbrique dans front_of_house. L’arbre montre également que certains modules sont frères et sœurs (siblings), ce qui signifie qu’ils sont définis dans le même module ; hosting et serving sont des modules frères définis au sein de front_of_house. Si le module A est contenu dans le module B, nous disons que le module A est l’enfant du module B et que le module B est le parent du module A. Notez que l’arbre de modules entier est enraciné sous le module implicite nommé crate.

L’arbre de modules pourrait vous rappeler l’arborescence de répertoires du système de fichiers de votre ordinateur ; c’est une comparaison très pertinente ! Tout comme les répertoires dans un système de fichiers, vous utilisez les modules pour organiser votre code. Et tout comme les fichiers dans un répertoire, nous avons besoin d’un moyen de trouver nos modules.