Cargo et les crates en Rust
Sur la page Débuter avec Rust, vous avez utilisé cargo new et cargo run. Ce n’était qu’un début. Cargo est l’outil de compilation de Rust et son gestionnaire de paquets. Il compile votre code, lance vos tests, formate vos fichiers et télécharge les bibliothèques écrites par d’autres.
Ces bibliothèques s’appellent des crates. Voyez-les comme les applications d’un magasin d’applications : quelqu’un a déjà résolu un problème (lire du JSON, faire des requêtes web, analyser des dates), et vous pouvez ajouter sa solution à votre projet en une seule commande. Cette page vous montre comment ça marche, et comment choisir une bonne crate.
Crates et paquets
Section intitulée « Crates et paquets »Deux mots reviennent sans cesse :
- Une crate est une unité de code Rust que le compilateur construit. C’est soit un binaire (un programme que vous pouvez lancer, qui commence dans
src/main.rs), soit une bibliothèque (du code destiné à d’autres programmes, qui commence danssrc/lib.rs). - Un paquet (package) est ce que crée
cargo new: un dossier avec un fichierCargo.tomlet une ou plusieurs crates à l’intérieur.
Dans la vie de tous les jours, on dit « crate » pour les deux. Quand quelqu’un dit « utilise la crate serde », il veut dire « ajoute le paquet serde de crates.io à ton projet ».
Le contenu de Cargo.toml
Section intitulée « Le contenu de Cargo.toml »Chaque paquet a un fichier Cargo.toml. Il est écrit en TOML, un format de configuration simple. Voici ce que vous donne cargo new hello_rust :
[package]name = "hello_rust"version = "0.1.0"edition = "2024"
[dependencies][package]décrit votre projet.nameetversionsont ce que les autres verraient si vous le publiiez.editionchoisit l’édition du langage Rust à utiliser. Les éditions (2018, 2021, 2024) permettent à Rust de s’améliorer sans casser le code existant. Gardez simplement ce quecargo newa écrit.[dependencies]liste les crates que votre code utilise. Au départ, la section est vide.
Quand un projet grandit, d’autres sections peuvent apparaître :
[dependencies]serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"
[dev-dependencies]# utilisées seulement par les tests et les exemples, pas par le vrai programmepretty_assertions = "1.4"
[profile.release]# réglages pour `cargo build --release`opt-level = 3Ajouter une dépendance avec cargo add
Section intitulée « Ajouter une dépendance avec cargo add »Vous pouvez modifier [dependencies] à la main, mais le plus simple est cargo add. Cette commande trouve la dernière version sur crates.io et écrit la ligne pour vous :
cargo add serde_jsonCertaines crates ont des parties optionnelles appelées features (fonctionnalités). Elles sont désactivées par défaut pour garder votre build léger. Vous les activez avec --features :
cargo add serde --features deriveAprès ces deux commandes, votre Cargo.toml contiendra quelque chose comme ceci :
[dependencies]serde = { version = "1.0.228", features = ["derive"] }serde_json = "1.0.145"Utilisez maintenant la crate dans votre code. Le prochain cargo run la télécharge, la compile une fois, et la lie à votre programme :
use serde_json::json;
fn main() { let crab = json!({ "legs": 10, "name": "Ferris" });
println!("{}", crab); println!("Nom : {}", crab["name"]);}Sortie :
{"legs":10,"name":"Ferris"}Nom : "Ferris"Les guillemets autour de "Ferris" sont là parce que crab["name"] est toujours une valeur JSON, pas une simple chaîne Rust.
Le Rust Playground inclut déjà les crates les plus populaires, donc ce code y fonctionne sans cargo add. Sur votre machine, il faut d’abord passer par cargo add serde_json. Vous en apprendrez beaucoup plus sur cette crate à la page JSON avec Serde.
Pour retirer une crate dont vous n’avez plus besoin, lancez cargo remove serde_json.
Exigences de version et Cargo.lock
Section intitulée « Exigences de version et Cargo.lock »Quand vous écrivez serde_json = "1.0", vous ne demandez pas exactement la version 1.0. Vous dites « n’importe quelle version compatible avec 1.0 ». Cargo suit le versionnage sémantique (SemVer) : une version s’écrit MAJEUR.MINEUR.CORRECTIF, et seul un changement du numéro majeur a le droit de casser votre code.
| Vous écrivez | Cargo peut utiliser | Pourquoi |
|---|---|---|
"1.0" (identique à "^1.0") |
>=1.0.0 et <2.0.0 |
toutes les versions 1.x doivent être compatibles |
"1.4.2" |
>=1.4.2 et <2.0.0 |
au moins 1.4.2, mais toujours 1.x |
"0.4" |
>=0.4.0 et <0.5.0 |
avant 1.0, c’est le numéro mineur qui compte comme cassant |
"~1.4" |
>=1.4.0 et <1.5.0 |
seulement les correctifs |
"=1.4.2" |
exactement 1.4.2 |
rarement nécessaire |
Alors, qui décide de la version exacte ? C’est le rôle de Cargo.lock. À la première compilation, Cargo choisit les versions les plus récentes qui respectent vos exigences et les note dans Cargo.lock. Toutes les compilations suivantes utilisent exactement les mêmes versions, sur votre machine comme sur celles des autres.
- Ne modifiez pas
Cargo.lockà la main. C’est Cargo qui le gère. - Ajoutez-le à git, surtout pour les programmes. Ainsi, vos coéquipiers et votre CI compilent exactement ce que vous avez compilé.
cargo updatepasse aux versions les plus récentes qui respectent encoreCargo.toml, et réécritCargo.lock. Il ne saute jamais à une nouvelle version majeure : pour ça, c’est vous qui modifiezCargo.toml.
Trouver une bonne crate
Section intitulée « Trouver une bonne crate »Trois sites vous serviront encore et encore :
- crates.io est le registre officiel. Toutes les crates publiques s’y trouvent. Cherchez un mot comme « csv » ou « date ».
- docs.rs héberge la documentation de chaque crate, générée automatiquement. Par exemple, docs.rs/serde_json.
- lib.rs est un index non officiel, plus rapide, qui classe les crates par catégorie. Idéal pour explorer.
Beaucoup de crates résolvent le même problème : comment choisir ? Avant d’en ajouter une, prenez une minute pour vérifier :
- Les téléchargements. Les crates populaires ont des millions de téléchargements. Cela veut dire que beaucoup de gens ont trouvé et corrigé les bugs avant vous.
- Les mises à jour récentes. Regardez la date de la dernière version. Une crate qui n’a pas bougé depuis des années peut très bien convenir (certaines sont simplement « terminées »), mais vérifiez que son dépôt n’est pas plein de tickets sans réponse.
- La documentation. Ouvrez sa page sur docs.rs. Les bonnes crates ont une page d’accueil claire avec un exemple à copier.
- Une version 1.0 ou plus. Ce n’est pas obligatoire, mais cela indique que l’auteur promet une API stable.
- Peu de dépendances. Chaque crate que vous ajoutez apporte ses propres dépendances. Plus c’est petit, plus il est facile de lui faire confiance.
En cas de doute, cherchez « rust » suivi de votre problème, et regardez quelle crate la communauté recommande le plus souvent.
Les commandes Cargo essentielles
Section intitulée « Les commandes Cargo essentielles »Inutile de les apprendre par cœur. Gardez ce tableau sous la main, et vous les retiendrez à force de les utiliser :
| Commande | Ce qu’elle fait |
|---|---|
cargo new my_app |
Crée un nouveau projet binaire (ajoutez --lib pour une bibliothèque) |
cargo build |
Compile dans target/debug/ |
cargo run |
Compile et lance (les arguments se passent après -- : cargo run -- input.txt) |
cargo check |
Cherche les erreurs sans produire de programme. Bien plus rapide que build |
cargo test |
Lance vos tests (voir Les tests) |
cargo fmt |
Formate tout votre code dans le style standard |
cargo clippy |
Lance le linter. Il suggère du code plus simple et plus idiomatique |
cargo doc --open |
Génère la documentation HTML de votre projet et de chaque dépendance, puis l’ouvre |
cargo update |
Met à jour Cargo.lock vers les versions compatibles les plus récentes |
cargo build --release |
Compile un programme optimisé dans target/release/ |
Une bonne habitude : lancez souvent cargo check pendant que vous écrivez, et cargo fmt et cargo clippy avant de faire un commit. cargo doc --open est aussi une petite pépite méconnue : il vous donne la documentation des versions exactes des crates que vous utilisez, et il fonctionne hors ligne.
Compilation debug ou release
Section intitulée « Compilation debug ou release »Par défaut, Cargo produit un build debug. Il compile vite et garde des vérifications de sécurité supplémentaires, comme une panic quand un entier déborde. C’est parfait pendant le développement.
Un build release (cargo build --release ou cargo run --release) prend plus de temps à compiler, mais s’exécute bien plus vite, souvent 10 fois plus ou davantage. Utilisez-le quand vous distribuez votre programme ou mesurez ses performances.
Votre code peut même savoir dans quel type de build il se trouve :
fn main() { if cfg!(debug_assertions) { println!("Build debug : vérifications supplémentaires activées"); } else { println!("Build release : optimisé pour la vitesse"); }}Sortie :
Build debug : vérifications supplémentaires activéesDans le Playground, vous pouvez passer de « Debug » à « Release » dans le menu, puis relancer pour voir l’autre ligne.
Crate binaire et crate bibliothèque ensemble
Section intitulée « Crate binaire et crate bibliothèque ensemble »Un paquet peut contenir une bibliothèque et un binaire en même temps. C’est une organisation très courante : vous gardez la vraie logique dans src/lib.rs, et src/main.rs reste petit et se contente de l’appeler :
greeter/├── Cargo.toml└── src/ ├── lib.rs # la crate bibliothèque : la logique réutilisable └── main.rs # la crate binaire : le programme que vous lancezpub fn greet(name: &str) -> String { format!("Bonjour, {name} !")}fn main() { // la bibliothèque porte le nom du paquet println!("{}", greeter::greet("Ferris"));}Pourquoi découper ainsi ? Le code de lib.rs est facile à tester, et d’autres programmes (ou un second binaire) peuvent le réutiliser. Le Playground n’a qu’un seul fichier, mais vous pouvez retrouver la même idée avec un pub mod, comme sur la page Les modules :
mod greeter { pub fn greet(name: &str) -> String { format!("Bonjour, {name} !") }}
fn main() { println!("{}", greeter::greet("Ferris"));}Sortie :
Bonjour, Ferris !Un aperçu des workspaces
Section intitulée « Un aperçu des workspaces »Quand un projet grandit et se compose de plusieurs paquets qui vont ensemble (par exemple une application en ligne de commande, un serveur web et une bibliothèque commune), vous pouvez les regrouper dans un workspace (espace de travail). Ils partagent un seul Cargo.lock et un seul dossier target/ : tout se compile ensemble et utilise les mêmes versions de dépendances.
Le Cargo.toml à la racine liste les membres :
[workspace]members = ["app", "core"]resolver = "3"my_project/├── Cargo.toml # le fichier de workspace ci-dessus├── Cargo.lock # partagé par tous les membres├── app/ # un paquet binaire└── core/ # un paquet bibliothèqueEnsuite, cargo build à la racine compile tout, et cargo run -p app lance un seul membre. Vous n’en aurez pas besoin pour vos premiers projets, mais c’est utile de savoir le reconnaître en lisant le code des autres.
Erreurs courantes
Section intitulée « Erreurs courantes »Utiliser une crate sans l’avoir ajoutée. Si vous écrivez use serde_json::json; mais avez oublié cargo add serde_json, le compilateur ne la trouve pas :
error[E0432]: unresolved import `serde_json` --> src/main.rs:1:5 |1 | use serde_json::json; | ^^^^^^^^^^ use of unresolved module or unlinked crate `serde_json` | = help: you might be missing a crate named `serde_json`Le compilateur vous dit que l’import serde_json est introuvable et qu’il vous manque sans doute une crate de ce nom. La solution : lancer cargo add serde_json. Vérifiez aussi l’orthographe : les noms de crates utilisent - ou _, et dans le code, un - devient toujours _.
Oublier une feature. Si #[derive(Serialize)] indique qu’il ne trouve pas la macro derive, vous avez probablement ajouté serde sans --features derive. Relancez cargo add serde --features derive : la commande met à jour la ligne existante.
Mesurer la vitesse avec un build debug. Si votre programme vous paraît lent, essayez cargo run --release avant de toucher au code.
Exercices
Section intitulée « Exercices »1. Utiliser une crate depuis le Playground
Section intitulée « 1. Utiliser une crate depuis le Playground »Utilisez la macro json! de serde_json pour décrire une crate avec trois clés : downloads (un nombre), name et version. Affichez la valeur entière, puis seulement la version.
Indice : reprenez la forme de l’exemple crab dans Ajouter une dépendance avec cargo add. Sur votre machine, lancez d’abord cargo add serde_json ; le Playground l’a déjà.
Voir la solution
use serde_json::json;
fn main() { let krate = json!({ "downloads": 500, "name": "serde_json", "version": "1.0" });
println!("{}", krate); println!("Version : {}", krate["version"]);}Sortie :
{"downloads":500,"name":"serde_json","version":"1.0"}Version : "1.0"La variable s’appelle krate parce que crate est un mot réservé en Rust. Cette orthographe est une blague classique dans le code Rust. Comme avant, la version s’affiche avec des guillemets car c’est toujours une valeur JSON ; la page JSON avec Serde montre comment en extraire du texte brut.
2. Des messages uniquement en mode debug
Section intitulée « 2. Des messages uniquement en mode debug »Écrivez une fonction debug_log(msg: &str) qui affiche [debug] suivi du message, mais seulement dans un build debug. Utilisez-la dans main autour d’un petit calcul, et affichez le résultat normalement.
Indice : entourez le println! d’un if cfg!(debug_assertions) { ... }, comme dans Compilation debug ou release.
Voir la solution
fn debug_log(msg: &str) { if cfg!(debug_assertions) { println!("[debug] {msg}"); }}
fn main() { debug_log("début du calcul"); let mut total = 0; for n in 1..=10 { total += n; } debug_log("calcul terminé");
println!("La somme de 1 à 10 vaut {total}");}Sortie :
[debug] début du calcul[debug] calcul terminéLa somme de 1 à 10 vaut 55Passez le Playground en « Release » (ou utilisez cargo run --release en local) et relancez : seule la dernière ligne s’affiche. Pour de vrais projets, la page Journalisation présente des crates qui font ça proprement, avec des niveaux comme debug et info.
Récapitulatif
Section intitulée « Récapitulatif »- Cargo compile, lance, teste et formate votre code, et télécharge des crates depuis crates.io.
Cargo.tomlcontient ce que vous demandez ;Cargo.lockenregistre les versions exactes obtenues. Ajoutez les deux à git.cargo add nom --features xajoute une dépendance."1.0"veut dire « n’importe quelle version 1.x compatible ».- Jugez une crate à ses téléchargements, ses mises à jour récentes et sa bonne documentation sur docs.rs.
- Utilisez
cargo checkpendant l’écriture,cargo fmtetcargo clippyavant un commit, et--releasepour la vitesse. - Mettez la logique réutilisable dans
src/lib.rset gardezsrc/main.rspetit.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Dans Cargo.toml, que signifie serde_json = "1.0" ?
2. Quel fichier enregistre les versions exactes de chaque dépendance utilisée par votre build ?
3. Vous voulez chercher les erreurs dans votre code le plus vite possible, sans produire de programme. Quelle commande convient le mieux ?
4. Comment ajouter serde avec sa feature derive activée ?
Score : 0 / 4
Pour aller plus loin
Section intitulée « Pour aller plus loin »- The Cargo Book (en anglais) : le guide officiel et la référence de tout ce que fait Cargo.
- Specifying Dependencies — The Cargo Book (en anglais) : toutes les façons d’écrire une exigence de version, ainsi que les dépendances git et locales.
cargo add— The Cargo Book (en anglais) : toutes les options decargo add, comme--dev,--featureset--no-default-features.- More about Cargo and Crates.io — The Rust Book (en anglais) : les profils de release, la publication de votre propre crate et l’installation d’outils avec
cargo install. - Cargo Workspaces — The Rust Book (en anglais) : un workspace pas à pas, avec un binaire et deux bibliothèques.