Retour au cours

backend / rust

Écosystème : cargo workspaces et publier une crate

Leçon 221 exercice

Explication

Ce que vous allez apprendre

  • Organiser un projet Rust en plusieurs crates réunis sous un workspace Cargo
  • Cibler un membre précis du workspace pour compiler, tester ou exécuter
  • Préparer les métadonnées obligatoires d'une crate destinée à être publiée
  • Publier une crate sur crates.io avec cargo publish en toute sécurité
  • Générer une documentation HTML exploitable avec cargo doc

Dans quel contexte ?

Une startup qui développe une application backend en Rust se retrouve avec un seul énorme crate mélangeant la logique métier, l'API HTTP et un outil CLI d'administration interne. À chaque modification de l'API, tout le projet — y compris le CLI qui n'a rien à voir — doit être recompilé, et les tests unitaires de la logique métier se mélangent avec les tests d'intégration de l'API. Un workspace Cargo résout exactement ce problème en séparant ces responsabilités en crates indépendants qui partagent un seul Cargo.lock.

D'abord, comprendre ce qu'un workspace apporte

Un workspace regroupe plusieurs crates Rust sous un seul fichier Cargo.toml racine, qui ne contient pas de code lui-même mais liste les membres (core, api, cli par exemple). L'avantage principal : un seul Cargo.lock partagé garantit que tous les crates du workspace utilisent exactement les mêmes versions de leurs dépendances communes, évitant les incohérences de versions.

CommandeEffet
cargo build --workspaceCompile tous les membres du workspace
cargo test -p coreTeste uniquement le crate core
cargo run -p apiLance uniquement le binaire api
cargo doc --openGénère et ouvre la documentation HTML de tous les crates

Prérequis

Cette leçon suppose une bonne compréhension d'un Cargo.toml classique et de la notion de dépendance, vues en tout début de parcours.

Une fois le workspace en place, les dépendances entre crates deviennent locales

Un crate du workspace peut dépendre d'un autre simplement via un chemin relatif (core = { path = "../core" }), sans avoir besoin de le publier sur crates.io au préalable. C'est exactement ce qui permet à api et cli de partager la même logique métier définie dans core, sans duplication de code.

Il reste un problème à résoudre : comment partager du code avec la communauté

Quand une bibliothèque devient suffisamment générique et utile pour être réutilisée en dehors du projet, l'étape suivante est de la publier sur crates.io, le registre officiel de paquets Rust. Avant cela, un Cargo.toml de bibliothèque doit contenir des métadonnées obligatoires : description, license, et idéalement repository et keywords pour la découvrabilité.

Piège courant

cargo publish est irréversible pour un numéro de version donné : une fois publiée, une version ne peut jamais être supprimée ni republiée avec le même numéro (seul un cargo yank la marque comme déconseillée, sans la retirer complètement). Utilise systématiquement cargo publish --dry-run avant la publication réelle pour vérifier que tout est correct.

Enfin, documenter ce qu'on publie

Les commentaires de documentation (/// pour un élément, //! pour tout un module) ne sont pas de simples commentaires : cargo doc les transforme en documentation HTML navigable, et les blocs de code qu'ils contiennent (comme dans l'exemple additionner) sont même exécutés automatiquement comme des tests par cargo test — ce sont les doctests.

Bonne pratique

Écris toujours au moins un exemple # Examples dans la documentation d'une fonction publique. En plus d'aider les utilisateurs de ta crate, cet exemple sera vérifié à chaque cargo test, garantissant que ta documentation ne se périme jamais silencieusement.

Maintenant que tu sais organiser et publier du code Rust à l'échelle d'un écosystème, la dernière étape du parcours consiste à pousser Rust dans son domaine de prédilection historique : la programmation système bas niveau, sujet de la leçon finale.

Commandes & code

Écosystème : cargo workspaces et publier une crate

Organiser un projet multi-crates et partager du code réutilisable via crates.io.

toml
# Cargo.toml a la racine : declare un workspace regroupant plusieurs crates
[workspace]
resolver = "2"
members = [
    "core",
    "api",
    "cli",
]
bash
mon-workspace/
  Cargo.toml         <- workspace racine
  Cargo.lock          <- UN SEUL lockfile partage par tout le workspace
  core/
    Cargo.toml
    src/lib.rs         <- logique metier partagee
  api/
    Cargo.toml         <- depend de core via chemin local
    src/main.rs
  cli/
    Cargo.toml         <- depend de core aussi
    src/main.rs
toml
# api/Cargo.toml : dependance locale vers un autre crate du workspace
[package]
name = "api"
version = "0.1.0"
edition = "2021"

[dependencies]
core = { path = "../core" }
tokio = { version = "1", features = ["full"] }
bash
# Commandes utiles sur un workspace
cargo build --workspace           # compile tous les membres
cargo test -p core                  # teste uniquement le crate "core"
cargo run -p api                     # lance uniquement le binaire "api"
toml
# Cargo.toml d'une bibliotheque destinee a etre publiee
[package]
name = "ma-super-lib"
version = "0.1.0"
edition = "2021"
description = "Une bibliotheque Rust qui fait X"
license = "MIT OR Apache-2.0"
repository = "https://github.com/user/ma-super-lib"
readme = "README.md"
keywords = ["exemple", "utilitaire"]
categories = ["algorithms"]
bash
# Publier une crate sur crates.io
cargo login <token>          # authentification (token depuis crates.io)
cargo publish --dry-run      # verification sans publication reelle
cargo publish                 # publication definitive, IRREVERSIBLE (versions ne peuvent etre supprimees)

# Versionner correctement (semver strict, verifie par cargo)
cargo set-version 0.2.0
rust
// Documenter une crate publiee : les doc-comments generent la doc via `cargo doc`
//! # Ma Super Lib
//!
//! Documentation du niveau crate (au tout debut de lib.rs)

/// Additionne deux nombres.
///
/// # Examples
/// ```
/// assert_eq!(ma_super_lib::additionner(2, 3), 5);
/// ```
pub fn additionner(a: i32, b: i32) -> i32 {
    a + b
}
bash
cargo doc --open   # genere et ouvre la documentation HTML localement

Résumé

  • Un workspace regroupe plusieurs crates sous un Cargo.lock unique, avec des dépendances locales par chemin.
  • cargo build/test/run -p <nom> cible un membre précis du workspace.
  • cargo publish envoie une crate sur crates.io — irréversible pour un numéro de version donné.
  • Les doc-comments (///, //!) génèrent une documentation HTML et des doctests exécutables via cargo doc/cargo test.

Exercices pratiques

1 disponible
1

Mission : une publication précipitée et un workspace à réorganiser

Objectif : Réagir correctement à une crate publiée par erreur avec une faille, puis structurer un workspace multi-crates et anticiper les contraintes de publication d'un crate dépendant.

Contexte

Un développeur vient d'exécuter cargo publish sur ma-super-lib en version 0.1.0. Quelques minutes plus tard, il découvre une faille de sécurité dans le code qu'il vient de publier sur crates.io.

Séparément, son équipe travaille sur un projet mélangé dans un seul crate (logique métier, API HTTP, CLI d'admin) et veut le réorganiser en workspace avec trois membres : core, api et cli. À terme, elle envisage aussi de publier api séparément sur crates.io, alors que api dépend aujourd'hui de core uniquement via un chemin local (path = "../core").

Résoudre l’exercice →