Phase 4Organisation du code

#13 Modules et crates

mod, use, pub, crate

Qu'est-ce qu'un module ?

Les modules organisent votre code en groupes logiques. Ils contrôlent la visibilité (ce qui est public ou privé) et la structure de votre projet. Pensez-y comme des dossiers pour votre code.

En Rust, chaque fichier est implicitement un module, et vous pouvez créer des sous-modules à l'intérieur.

Le mot-clé mod

Le mot-clé mod déclare un nouveau module. Vous pouvez définir un module directement dans un fichier ou le lier à un fichier séparé.

Module en ligne
// Définir un module en ligne
mod salutations {
    pub fn bonjour() {
        println!("Bonjour depuis le module !");
    }

    fn message_prive() {
        println!("Ceci est privé");
    }
}

fn main() {
    salutations::bonjour();       // OK
    // salutations::message_prive(); // ERREUR : privé !
}

Modules imbriqués

Modules imbriqués
mod reseau {
    pub mod connexion {
        pub fn etablir() {
            println!("Connexion établie !");
        }
    }

    pub mod message {
        pub fn envoyer(msg: &str) {
            println!("Message envoyé : {}", msg);
        }
    }
}

fn main() {
    reseau::connexion::etablir();
    reseau::message::envoyer("Bonjour");
}

Visibilité (pub)

Par défaut, tout est privé en Rust. Le mot-clé pub rend un élément accessible depuis l'extérieur du module.

Niveaux de visibilité
mod serveur {
    pub struct Config {
        pub port: u16,        // champ public
        adresse: String,      // champ privé !
    }

    impl Config {
        // Constructeur public pour initialiser les champs privés
        pub fn nouveau(port: u16, adresse: &str) -> Config {
            Config {
                port,
                adresse: String::from(adresse),
            }
        }

        pub fn afficher(&self) {
            println!("{}:{}", self.adresse, self.port);
        }
    }
}

fn main() {
    let config = serveur::Config::nouveau(8080, "localhost");
    println!("Port : {}", config.port);  // OK, champ pub
    // println!("{}", config.adresse);    // ERREUR : champ privé
    config.afficher();                    // OK, méthode pub
}

Variantes de visibilité : pub(crate) rend visible dans tout le crate, pub(super) dans le module parent uniquement.

Le mot-clé use

use crée des raccourcis pour éviter de répéter les chemins complets. C'est similaire à un import dans d'autres langages.

Utiliser use
mod reseau {
    pub mod connexion {
        pub fn etablir(port: u16) {
            println!("Connexion établie sur le port {}", port);
        }
    }
}

// Importer avec use
use reseau::connexion;

fn main() {
    // Au lieu de reseau::connexion::etablir(8080)
    connexion::etablir(8080);
}

// Autres formes de use :
// use reseau::connexion::etablir;   // importer la fonction directement
// use reseau::connexion as conn;     // renommer avec as
// use std::collections::{HashMap, HashSet}; // importer plusieurs éléments

Découper en fichiers

Pour les projets plus grands, chaque module peut être un fichier séparé. Rust cherche automatiquement le fichier correspondant.

Structure du projet
src/
├── main.rs
├── reseau.rs         # module reseau
└── reseau/
    ├── connexion.rs  # sous-module reseau::connexion
    └── message.rs    # sous-module reseau::message
src/main.rs
mod reseau;  // charge src/reseau.rs

use reseau::connexion;
use reseau::message;

fn main() {
    connexion::etablir(8080);
    message::envoyer("Bonjour depuis Rust");
}
src/reseau.rs
pub mod connexion;  // charge src/reseau/connexion.rs
pub mod message;    // charge src/reseau/message.rs

pub fn bienvenue() {
    println!("Bienvenue sur le réseau !");
}
src/reseau/connexion.rs
pub fn etablir(port: u16) {
    println!("Connexion établie sur le port {}", port);
}

À vous de jouer

Essayez les commandes ci-dessous pour compiler et exécuter le programme :

terminal — cargo
user@stemlegacy:~/modules-demo$