Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Les espaces de travail de cargo

Dans le chapitre 12, nous avons construit un paquet qui comprenait une crate binaire et une crate de bibliothèque. Au fur et à mesure que votre projet se développe, vous pourrez constater que la crate de bibliothèque continue de s’agrandir et vous voudriez alors peut-être diviser votre paquet en plusieurs crates de bibliothèque. Cargo a une fonctionnalité qui s’appelle les espaces de travail qui peuvent aider à gérer plusieurs paquets liés qui sont développés en tandem.

Créer un espace de travail

Un espace de travail est un jeu de paquets qui partagent tous le même Cargo.lock et le même répertoire de sortie. Créons donc un projet en utilisant un espace de travail — nous allons utiliser du code trivial afin de nous concentrer sur la structure de l’espace de travail. Il existe plusieurs façons de structurer un espace de travail, nous allons simplement vous montrer une manière commune d’organisation. Nous allons avoir un espace de travail contenant un binaire et deux bibliothèques. Le binaire, qui devrait fournir les fonctionnalités principales, va dépendre des deux bibliothèques. Une bibliothèque va fournir une fonction ajouter_un, et l’autre bibliothèque, une fonction ajouter_deux. Ces trois crates feront partie du même espace de travail. Nous allons commencer par créer un nouveau répertoire pour cet espace de travail :

$ mkdir ajout
$ cd ajout

Ensuite, dans le répertoire ajout, nous créons le fichier Cargo.toml qui va configurer l’intégralité de l’espace de travail. Ce fichier n’aura pas de section [package]. À la place, il commencera par une section [workspace] qui va nous permettre d’ajouter des membres à l’espace de travail. Nous veillons également à utiliser la version qui soit la dernière et la plus élevée de l’algorithme de résolution de Cargo dans notre espace de travail en définissant la valeur resolver à "3" :

Fichier : Cargo.toml

[workspace]
resolver = "3"

Ensuite, nous allons créer la crate binaire additionneur en lançant cargo new dans le répertoire ajout :

$ cargo new additionneur
     Created binary (application) `additionneur` package
      Adding `additionneur` as member of workspace at `file:///projects/ajout`

L’exécution de cargo new dans un espace de travail ajoute aussi automatiquement le paquet récemment créé à la clé members de la définition [workspace] dans le fichier Cargo.toml, comme suit :

[workspace]
resolver = "3"
members = ["additionneur"]

À partir de ce moment, nous pouvons compiler l’espace de travail en lançant cargo build. Les fichiers dans votre répertoire ajout devraient se présenter ainsi :

├── Cargo.lock
├── Cargo.toml
├── additionneur
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

L’espace de travail a un répertoire target au niveau le plus haut pour y placer les artéfacts compilés ; le paquet additionneur n’a pas son propre répertoire target. Même si nous lancions cargo build à l’intérieur du répertoire additionneur, les artéfacts compilés finirons toujours dans ajout/target plutôt que dans ajout/additionneur/target. Cargo organise ainsi le répertoire target car les crates d’un espace de travail sont censés dépendre l’une de l’autre. Si chaque crate avait son propre répertoire target, chaque crate devrait recompiler chacune des autres crates présentes dans l’espace de travail pour avoir les artéfacts dans son propre répertoire target. En partageant un seul répertoire target, les crates peuvent éviter des recompilations inutiles.

Créer le second paquet dans l’espace de travail

Ensuite, créons un autre paquet, membre de l’espace de travail et appelons-le ajouter_un. Puis générons une nouvelle crate de bibliothèque ajouter_un :

$ cargo new ajouter_un --lib
     Created library `ajouter_un` package
      Adding `add_one` as member of workspace at `file:///projects/add`

Le Cargo.toml du niveau le plus haut va maintenant inclure le chemin vers ajouter_un dans la liste members :

Fichier : Cargo.toml

[workspace]
resolver = "3"
members = ["additionneur", "ajouter_un"]

Votre répertoire ajout devrait maintenant avoir ces répertoires et fichiers :

├── Cargo.lock
├── Cargo.toml
├── ajouter_un
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── additionneur
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

Dans le fichier ajouter_un/src/lib.rs, ajoutons une fonction ajouter_un :

Fichier : ajouter_un/src/lib.rs

pub fn ajouter_un(x: i32) -> i32 {
    x + 1
}

Nous pouvons maintenant faire en sorte que le paquet additionneur qui contient notre binaire dépende du paquet ajouter_un, qui contient notre bibliothèque. D’abord, nous devons ajouter un chemin de dépendance à ajouter_un dans additionneur/Cargo.toml.

Fichier : ajouter_un/Cargo.toml

[dependencies]
ajouter_un = { path = "../ajouter_un" }

Cargo ne fait pas la supposition que les crates d’un espace de travail dépendent l’une de l’autre, donc vous devez être explicites sur les relations de dépendance.

Ensuite, utilisons la fonction ajouter_un (de la crate ajouter_un) dans la crate additionneur. Ouvrez le fichier additionneur/src/main.rs et changez la fonction main pour appeler la fonction ajouter_un, comme dans l’encart 14-7.

Filename: adder/src/main.rs
fn main() {
    let nombre = 10;
    println!("Hello, world! {nombre} plus un vaut {} !", ajouter_un::ajouter_un(nombre));
}
Listing 14-7: Using the add_one library crate from the adder crate

Compilons l’espace de travail en lançant cargo build dans le niveau le plus haut du répertoire ajout !

$ cargo build
   Compiling ajouter_un v0.1.0 (file:///projects/ajout/ajouter_un)
   Compiling additionneur v0.1.0 (file:///projects/ajout/additionneur)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s

Pour lancer la crate binaire à partir du répertoire ajout, nous pouvons préciser quel paquet nous souhaitons exécuter dans l’espace de travail en utilisant l’argument -p suivi du nom du paquet avec cargo run :

$ cargo run -p additionneur
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/additionneur`
Hello, world ! 10 plus un vaut 11 !

Cela exécute le code de additionneur/src/main.rs, qui dépend de la crate ajouter_un.

Dépendre d’un paquet externe dans un espace de travail

Notez que l’espace de travail a un seul fichier Cargo.lock dans le niveau le plus haut plutôt que d’avoir un Cargo.lock dans chaque répertoire de chaque crate. Cela garantit que toutes les crates utilisent la même version de toutes les dépendances. Si nous ajoutons le paquet rand aux fichiers additionneur/Cargo.toml et ajouter_un/Cargo.toml, cargo va réunir les deux en une seule version de rand et enregistrer cela dans un seul Cargo.lock. Faire en sorte que toutes les crates de l’espace de travail utilisent la même dépendance signifie que les crates seront toujours compatibles l’une avec l’autre. Ajoutons la crate rand à la section [dependencies] du fichier ajouter_un/Cargo.toml pour utiliser la crate rand dans la crate ajouter_un :

Fichier : hello.html

[dependencies]
rand = "0.8.5"

Nous pouvons maintenant ajouter use rand; au fichier ajouter_un/src/lib.rs et compiler l’ensemble de l’espace de travail en lançant cargo build dans le répertoire ajout, ce qui va importer et compiler la crate rand. Nous devrions avoir un avertissement car nous n’avons pas utilisé le rand que nous avons introduit dans la portée :

$ cargo build
    Updating crates.io index
  Downloaded rand v0.8.5
   --snip--
   Compiling rand v0.8.5
   Compiling ajouter_un v0.1.0 (file:///projects/ajout/ajouter_un)
warning: unused import: `rand`
 --> ajouter_un/src/lib.rs:1:5
  |
1 | use rand;
  |     ^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: `ajouter_un` (lib) generated 1 warning (run `cargo fix --lib -p ajouter_un` to apply 1 suggestion)
   Compiling additionneur v0.1.0 (file:///projects/ajout/additionneur)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.95s

Le Cargo.lock du niveau le plus haut contient maintenant les informations de dépendance à rand pour ajouter_un. Cependant, même si rand est utilisé quelque part dans l’espace de travail, nous ne pouvons pas l’utiliser dans d’autres crates de l’espace de travail tant que nous n’ajoutons pas rand dans leurs fichiers Cargo.toml. Par exemple, si nous ajoutons use rand; dans le fichier additionneur/src/main.rs pour le paquet additionneur, nous allons avoir une erreur :

$ cargo build
  -- partie masquée ici --
   Compiling additionneur v0.1.0 (file:///projects/ajout/additionneur)
error[E0432]: unresolved import `rand`
 --> additionneur/src/main.rs:2:5
  |
2 | use rand;
  |     ^^^^ no external crate `rand`

Pour corriger cela, modifiez le fichier Cargo.toml pour le paquet additionneur et indiquez que rand est une dépendance de cette crate aussi. La compilation du paquet additionneur va rajouter rand à la liste des dépendances pour additionneur dans Cargo.lock, mais aucune copie supplémentaire de rand ne sera téléchargée. Cargo va s’assurer que toutes chaque crate de chaque paquet de l’espace de travail qui utilise le paquet rand utilisera la même version, tant qu’elles spécifieront des versions compatibles de rand, ce qui économise de l’espace et nous assurer que les crates dans l’espace de travail seront compatibles les unes avec les autres.

Si les crates présentes dans l’espace de travail spécifient des versions incompatibles d’une même dépendance, Cargo résoudra chacune d’entre elles, tout en s’efforçant de n’en résoudre que le moins possible.

Ajouter un test à l’espace de travail

Afin de procéder à une autre amélioration, ajoutons un test de la fonction ajouter_un::ajouter_un dans la crate ajouter_un :

Fichier : ajouter_un/src/lib.rs

pub fn ajouter_un(x: i32) -> i32 {
    x + 1
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn cela_fonctionne() {
        assert_eq!(3, ajouter_un(2));
    }
}

Lancez maintenant cargo test dans le niveau le plus haut du répertoire ajout. En exécutant cargo test dans un espace de travail structuré comme celui-ci, les tests de toutes les crates présentes dans l’espace de travail seront lancés :

$ cargo test
   Compiling ajouter_un v0.1.0 (file:///projects/ajout/ajouter_un)
   Compiling additionneur v0.1.0 (file:///projects/ajout/additionneur)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s
     Running unittests src/lib.rs (target/debug/deps/ajouter_un-93c49ee75dc46543)

running 1 test
test tests::cela_fonctionne ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/main.rs (target/debug/deps/ajouter_un-3a47283c568d2b6a)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests ajouter_un

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

La première section de la sortie indique que le test cela_fonctionne de la crate ajouter_un a réussi. La section suivante indique qu’aucun test n’a été trouvé dans la crate additionneur, puis la dernière section indique elle aussi qu’aucun test de documentation n’a été trouvé dans la crate ajouter_un. Lancer cargo test dans un espace de travail structuré comme celui-ci va exécuter les tests pour toutes les crates de cet espace de travail.

Nous pouvons aussi lancer des tests pour une crate en particulier dans un espace de travail à partir du répertoire du plus haut niveau en utilisant le drapeau -p et en renseignant le nom de la crate que nous voulons tester :

$ cargo test -p ajouter_un
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/ajouter_un-93c49ee75dc46543)

running 1 test
test tests::cela_fonctionne ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests ajouter_un

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Cette sortie montre que cargo test a lancé les tests uniquement pour la crate ajouter_un et n’a pas lancé les tests de la crate additionneur.

Si vous publiez les crates présentes dans l’espace de travail sur crates.io, chaque crate de l’espace de travail va avoir besoin d’être publiée de manière séparée. Comme pour cargo test, nous pouvons publier une crate donnée de notre espace de travail en utilisant le drapeau -p et en précisant le nom de la crate que nous voulons publier.

En guise d’entrainement supplémentaire, ajoutez une crate ajouter_deux dans cet espace de travail de la même manière que nous l’avons fait pour la crate ajouter_un !

Au fur et à mesure que votre projet se développe, pensez à utiliser un espace de travail : cela vous permettre de travailler avec des composants plus petits et plus aisés à comprendre qu’un gros tas de code. De plus, garder les crates dans un espace de travail peut améliorer la coordination entre elles si elles sont souvent modifiées ensemble.