7. Modules, Cargo et Organisation du Projet

L’organisation du code en Rust suit un système de modules simple mais puissant. Chaque fichier est un module, chaque dossier est un module avec un mod.rs (Rust 2018) ou un fichier du même nom. Cargo gère les dépendances, la compilation, les tests et la documentation.


7.1 Cargo — Le gestionnaire de projet

Créer un nouveau projet

cargo new mon_projet     # binaire (src/main.rs)
cargo new --lib ma_lib   # librairie (src/lib.rs)

Structure standard

mon_projet/
├── Cargo.toml        # métadonnées, dépendances
├── Cargo.lock        # versions figées (ne pas éditer)
├── src/
│   ├── main.rs       # point d'entrée du binaire
│   └── lib.rs        # point d'entrée de la librairie
├── tests/            # tests d'intégration
├── examples/         # exemples d'utilisation
├── benches/          # benchmarks
└── target/           # artefacts de compilation (gitignoré)

Cargo.toml

[package]
name = "gradient-aggregator"
version = "0.1.0"
edition = "2024"          # Rust edition (2021 = actuelle, 2024 = future)
description = "Aggrégation robuste de gradients pour apprentissage distribué"
authors = ["Arthur Danjou"]
license = "MIT"
repository = "https://github.com/arthurdanjou/gradient-aggregator"
 
[dependencies]
# Librairies
serde = { version = "1", features = ["derive"] }
serde_json = "1"
rayon = "1.8"
clap = { version = "4", features = ["derive"] }
ndarray = "0.16"
pyo3 = { version = "0.22", features = ["extension-module"] }
 
[dependencies.cudarc]
version = "0.12"
optional = true
 
[features]
default = []
cuda = ["cudarc"]  # feature optionnelle pour accélération GPU
 
[profile.release]
lto = true          # link-time optimization
codegen-units = 1   # meilleure optimisation

Commandes essentielles

CommandeEffet
cargo buildCompile (debug)
cargo build --releaseCompile (release, optimisé)
cargo runCompile + exécute
cargo testLance les tests
cargo doc --openGénère la documentation et l’ouvre
cargo checkVérifie la compilation sans produire de binaire (rapide)
cargo clippyLinter (nombreuses suggestions)
cargo fmtFormate le code
cargo add serdeAjoute une dépendance
cargo updateMet à jour Cargo.lock
cargo benchLance les benchmarks
cargo publishPublie sur crates.io

7.2 Modules

Déclaration de modules

// src/lib.rs
pub mod gar;           // charge src/gar.rs ou src/gar/mod.rs
mod utils;             // module privé (pas accessible depuis l'extérieur)
 
pub use gar::aggregate; // ré-exporter
// src/gar.rs — module "gar"
// Les sous-modules vont dans un dossier src/gar/
pub mod median;
pub mod krum;
mod internal;           // privé, même à l'intérieur du crate
 
pub trait Gar: Send + Sync {
    fn aggregate(&self, grads: &[Vec<f64>]) -> Vec<f64>;
}
// src/gar/median.rs
use crate::gar::Gar;
 
pub struct Median;
 
impl Gar for Median {
    fn aggregate(&self, grads: &[Vec<f64>]) -> Vec<f64> {
        let d = grads[0].len();
        let mut result = Vec::with_capacity(d);
        for j in 0..d {
            let mut col: Vec<_> = grads.iter().map(|g| g[j]).collect();
            col.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
            result.push(col[col.len() / 2]);
        }
        result
    }
}

Visibilité

Mot-cléVisible depuis
fn foo() (privé)Module courant et sous-modules
pub fn foo()Partout
pub(crate) fn foo()Dans tout le crate (pas à l’extérieur)
pub(super) fn foo()Module parent seulement
pub(in path::to::module)Module spécifique
// Règles de visibilité
mod outer {
    fn private() {}
    pub fn public() {}
    pub(crate) fn crate_visible() {}
    pub(super) fn parent_visible() {}
 
    mod inner {
        // Peut appeler private() (module enfant)
        // Peut appeler public() (pub)
        // Peut appeler crate_visible() (même crate)
        // Peut appeler parent_visible() (super = outer)
    }
}

Arborescence de modules

src/
├── lib.rs                  → crate racine
├── main.rs                 → binaire (si lib + bin)
├── gar/
│   ├── mod.rs              → pub mod gar;
│   ├── median.rs           → pub mod median;
│   ├── krum.rs             → pub mod krum;
│   ├── bulyan.rs           → pub mod bulyan;
│   └── internal.rs         → mod internal;  (pas exporté)
├── attack/
│   ├── mod.rs
│   └── alie.rs
├── io/
│   ├── mod.rs
│   ├── reader.rs
│   └── writer.rs
└── utils/
    ├── mod.rs
    ├── stats.rs
    └── linalg.rs

7.3 Workspaces — Projets multi-crates

# Cargo.toml (racine du workspace)
[workspace]
members = [
    "gradient-core",     # librairie cœur
    "gradient-pyo3",     # bindings Python
    "gradient-cli",      # CLI
    "gradient-bench",    # benchmarks
]
gradient-workspace/
├── Cargo.toml              # [workspace]
├── gradient-core/
│   ├── Cargo.toml
│   └── src/lib.rs
├── gradient-pyo3/
│   ├── Cargo.toml
│   └── src/lib.rs
├── gradient-cli/
│   ├── Cargo.toml
│   └── src/main.rs
└── gradient-bench/
    ├── Cargo.toml
    └── src/main.rs
# gradient-pyo3/Cargo.toml — dépend du core
[dependencies]
gradient-core = { path = "../gradient-core" }
pyo3 = { version = "0.22", features = ["extension-module"] }

7.4 Path et Utilisation

// Chemin absolu depuis la racine du crate
use crate::gar::median::Median;
 
// Chemin relatif
use super::gar::Gar;
use self::internal::helper;
 
// Alias
use crate::gar::krum::Krum as KrumGar;
 
// Ré-export
pub use crate::gar::median::Median;
 
// Import de tout ce qui est pub dans un module
use crate::gar::*;     // attention : peut importer des choses inattendues
 
// Import imbriqué (Rust 2018+)
use crate::gar::{self, median::Median, krum::Krum};

7.5 Bonnes Pratiques

Structure recommandée pour une librairie ML

gradient-core/
├── Cargo.toml
└── src/
    ├── lib.rs                    # pub mod gar, attack, io, utils
    ├── gar.rs                    # trait Gar, ré-exports
    ├── gar/
    │   ├── mod.rs
    │   ├── median.rs
    │   ├── krum.rs
    │   ├── trimmed_mean.rs
    │   └── bulyan.rs
    ├── attack.rs
    │   └── attack/
    │       ├── mod.rs
    │       └── alie.rs
    ├── types.rs                  # Gradient, WorkerId, etc.
    ├── io.rs
    │   ├── reader.rs
    │   └── writer.rs
    ├── utils/
    │   ├── mod.rs
    │   ├── stats.rs              # median, mean, variance
    │   └── linalg.rs             # dot, norm, flatten
    └── bench.rs                  # benchmarks internes

Règles

RègleJustification
Un fichier par conceptmedian.rs, krum.rs — pas de monolithe
Module racine = API publiquelib.rs ne fait que ré-exporter
pub(crate) pour l’interneFonctions de test, helpers partagés
mod privé par défautN’exporter que ce qui est nécessaire
Une seule responsabilitéChaque module fait une chose

🔗 Voir aussi