backend / rust
Modules et organisation de crate
Explication
Ce que vous allez apprendre
- Structurer un projet Rust réel avec des modules organisés en dossiers
- Contrôler la visibilité d'un élément avec
pub - Utiliser
crate::,super::etself::pour référencer un module précisément - Ré-exporter un sous-module pour simplifier les chemins d'import ailleurs
- Ajouter et explorer des dépendances externes via
cargo addetcargo tree
Dans quel contexte ?
Une équipe fait grandir un projet Rust qui tenait au départ dans un seul main.rs de quelques centaines de lignes, jusqu'à devenir un service métier complexe. Sans organisation en modules, ce fichier devient vite ingérable, et surtout, aucun mécanisme ne protège les détails d'implémentation internes contre une utilisation accidentelle depuis un autre endroit du code qui ne devrait pas y avoir accès.
D'abord, un module en Rust correspond généralement à un dossier ou un fichier
mod modeles; dans main.rs déclare un module local, que Rust va chercher soit dans modeles.rs, soit dans modeles/mod.rs. Cette seconde forme permet à un module d'être lui-même composé de plusieurs sous-fichiers, organisés dans un dossier dédié — la structure la plus courante pour un projet de taille réelle.
Une fois cette organisation posée, la visibilité se contrôle avec un seul mot-clé
Par défaut, tout est privé au module qui le déclare. pub rend un élément (fonction, struct, champ) visible depuis l'extérieur de son module. Une struct peut avoir certains champs publics et d'autres privés simultanément, un contrôle fin utile pour exposer un constructeur tout en cachant les détails internes.
| Préfixe de chemin | Signifie | Usage typique |
|---|---|---|
crate:: | Chemin absolu depuis la racine du crate | Référencer un module quelle que soit sa profondeur |
super:: | Remonte d'un niveau dans l'arborescence | Accéder au module parent |
self:: | Reste dans le module courant | Rarement nécessaire, souvent implicite |
Prérequis
Il faut avoir écrit au moins un petit projet avec cargo new (première leçon du cours) pour situer où les fichiers de modules s'insèrent concrètement dans l'arborescence générée.
Il reste un raccourci très pratique pour simplifier les imports ailleurs
pub use utilisateur::Utilisateur; dans un fichier mod.rs ré-exporte un type depuis un sous-module, permettant à l'extérieur d'écrire use modeles::Utilisateur plutôt que le chemin complet use modeles::utilisateur::Utilisateur. C'est un détail d'ergonomie qui améliore beaucoup la lisibilité des imports dans un gros projet.
Piège fréquent
Oublier pub sur un champ de struct qu'on veut manipuler depuis un autre module produit une erreur de compilation ("field is private"), même si la struct elle-même est publique. La visibilité en Rust se contrôle à chaque niveau — module, type, champ, méthode — indépendamment les uns des autres.
Enfin, comment ajouter une bibliothèque tierce à un projet
Bonne pratique
Utilise cargo add <nom-de-crate> plutôt que d'éditer manuellement Cargo.toml : cette commande résout automatiquement la dernière version compatible et met à jour le fichier correctement. cargo tree affiche ensuite l'arbre complet des dépendances, utile pour repérer une dépendance transitive inattendue ou en conflit de version.
Maintenant que tu sais organiser un projet Rust à grande échelle, la prochaine leçon aborde comment vérifier automatiquement que chaque module se comporte comme prévu : les tests.
Commandes & code
Modules et organisation de crate
Structurer un projet Rust réel avec des modules, une visibilité claire et des chemins explicites.
mon-projet/
Cargo.toml
src/
main.rs <- point d'entree du binaire
lib.rs <- point d'entree de la bibliotheque (si crate hybride)
config.rs
modeles/
mod.rs
utilisateur.rs
produit.rs
services/
mod.rs
utilisateur_service.rs// fichier: src/modeles/utilisateur.rs
pub struct Utilisateur {
pub nom: String,
email: String, // prive par defaut : accessible uniquement dans ce module
}
impl Utilisateur {
pub fn nouveau(nom: &str, email: &str) -> Self {
Self { nom: nom.to_string(), email: email.to_string() }
}
pub fn email(&self) -> &str { // accesseur public pour un champ prive
&self.email
}
}// fichier: src/modeles/mod.rs
pub mod utilisateur; // declare le sous-module et le rend accessible
pub mod produit;
pub use utilisateur::Utilisateur; // re-export : simplifie les chemins d'import ailleurs// fichier: src/main.rs
mod modeles; // declare le module local (cherche modeles/mod.rs ou modeles.rs)
mod services;
mod config;
use modeles::Utilisateur; // grace au re-export dans mod.rs
use services::utilisateur_service; // chemin complet vers le sous-module
fn main() {
let u = Utilisateur::nouveau("Alice", "alice@example.com");
println!("{} <{}>", u.nom, u.email());
utilisateur_service::afficher_bienvenue(&u);
}// fichier: src/services/utilisateur_service.rs
use crate::modeles::Utilisateur; // "crate::" = chemin absolu depuis la racine du crate
pub fn afficher_bienvenue(u: &Utilisateur) {
println!("Bienvenue, {} !", u.nom);
}
// super:: pour remonter d'un niveau dans l'arborescence de modules
// self:: pour rester dans le module courant# Dependances externes : ajoutees dans Cargo.toml, gerees par crates.io
cargo add serde --features derive
cargo add reqwest --features json
cargo tree # visualiser l'arbre complet des dependancesRésumé
pubrend un item public ; sanspub, il n'est visible que dans son module et ses enfants.mod.rs(ounom_module.rs) regroupe et ré-exporte les sous-modules d'un dossier.crate::,super::,self::sont les préfixes de chemins absolus/relatifs entre modules.cargo add+Cargo.tomlgèrent les dépendances externes hébergées sur crates.io.
Exercices pratiques
Mission : reorganiser un main.rs de 2000 lignes
Objectif : Diagnostiquer des erreurs de visibilite et de chemins de modules dans un projet en cours de decoupage, et les corriger.
Contexte
Une equipe decoupe enfin son gigantesque main.rs en modules. Elle a cree src/modeles/commande.rs avec une struct Commande, et src/services/facturation.rs qui doit l'utiliser. Apres le decoupage, le projet ne compile plus avec deux erreurs distinctes : 'field montant is private' dans facturation.rs, et 'unresolved import crate::modeles::Commande' dans main.rs, alors que pub mod commande; est bien present dans modeles/mod.rs.