Embarquez avec Cargo : destination Rust !

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
123
Mois de parution
février 2023
Spécialité(s)


Résumé

Cargo est le couteau suisse du développeur Rust : il sert à tout, depuis l'initialisation d'un projet à sa publication, en passant par la gestion de ses dépendances, l'exécution de tests, la vérification de la syntaxe, la compilation... Voyons un peu en détail les capacités de cet outil indispensable.


Body

Outre le compilateur ou l'interpréteur dans le cas de langages de scripts, tout langage moderne a besoin d'utilitaires pour faciliter la vie des développeurs. Mais la plupart du temps, ces outils ne font pas partie intégrante du langage, il faut les installer séparément. Le plus souvent, plusieurs sont nécessaires, chacun assumant une tâche ou un lot de fonctionnalités. Il se peut qu'aucune solution standard ne se soit imposée, auquel cas chaque développeur devra faire ses propres choix, ce qui peut provoquer des débats sans fin dans les équipes.

Rust évite cet écueil puisque, si vous suivez la procédure d'installation standard, il viendra accompagné de Cargo, qui, dans l'esprit qui anime le langage, se mettra au service du développeur pour assumer l'essentiel des tâches automatisables liées au développement, de la façon la plus efficace possible. Pour y parvenir, Cargo propose une méthodologie unique, dès lors partagée par tous les développeurs Rust de toutes les équipes du monde : plus de discussions, et gain de temps sur l'intégration des méthodes de travail pour les nouvelles recrues.

De belles promesses ? Voyons en détail ce qu'il en est.

1. Initialisation d'un projet ou « crate »

Les projets Rust sont structurés autour de la notion de crate. Une crate est la plus petite portion de code que le compilateur considère à la fois, et ce, même si vous demandez au compilateur de ne prendre en compte qu'un seul fichier : il est alors considéré comme une crate à lui tout seul.

D'emblée, Rust permet deux types de crates : une librairie ou un exécutable. Ainsi, nous avons initialisé le projet Mogul utilisé dans notre article précédent à l'aide de la commande :

$ cargo init mogul --lib
     Created library package

Lors de notre prochain article, nous allons créer un exécutable, proposant une interface en ligne de commande pour cette librairie. Nous initialisons cet autre projet à l'aide la commande :

$ cargo init mogul-cli
     Created binary (application) package

La sous-commande init est utilisée dans les deux cas. Par défaut, Cargo crée un paquet d'application, il suffit de lui passer l'option --lib pour faire une librairie.

Avec init, Cargo met en place une arborescence de base, assez simple pour le moment, mais qui connaîtra plus tard des augmentations au fur et à mesure des progrès du projet. Voyons à quoi ressemblent nos deux arborescences :

$ tree mogul mogul-cli
mogul
├── Cargo.toml
└── src
    └── lib.rs
mogul-cli
├── Cargo.toml
└── src
    └── main.rs

Il y a bien sûr un dossier pour chaque crate, de contenu très similaire : chacun contient un fichier Cargo.toml et un dossier src. La différence se situe dans ce dernier : chaque fois, Cargo a créé un fichier, mais ils diffèrent par leurs noms : lib.rs pour la librairie, main.rs pour l'application.

Mais y a-t-il des choses de cachées ?

$ ls -a mogul mogul-cli
mogul:
. .. Cargo.toml .git .gitignore src
 
mogul-cli:
. .. Cargo.toml .git .gitignore src

Eh oui ! Sans que nous ne lui ayons rien demandé, Cargo a initialisé des projets Git au sein des projets Rust, en fournissant même le .gitignore ! Toutefois, ceux-ci sont assez succincts. Dans le cas de l'application, il ne contient qu'une seule ligne pour exclure le dossier target : celui-ci n’est pas encore créé, mais il le sera à la première compilation et contiendra les binaires. Dans le cas de la librairie s'ajoute le fichier Cargo.lock : celui-ci n'existe pas encore non plus, son rôle sera d'indiquer quelle version de chaque dépendance a été retenue au moment de leur installation, afin de produire toujours les mêmes binaires et d'avoir un comportement constant. Pour quelle raison inclure le Cargo.lock dans un cas et pas dans l'autre ? La documentation s'explique là dessus [1] : autant il paraît désirable que l'application finale produise des binaires constants d'une instance à l'autre, autant il n'est pas souhaitable qu'une librairie soit liée trop rigoureusement à telle ou telle version d'une autre librairie. En effet, l'enchaînement des dépendances pourrait mener à des redondances (plusieurs versions requises de la même librairie), voire à des incompatibilités.

Examinons le contenu des autres fichiers générés. Les fichiers Cargo.toml sont quasiment identiques :

[package]
name = "mogul"
version = "0.1.0"
edition = "2021"
 
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
[dependencies]

La première section [package] donne des informations générales concernant le projet comme le nom, la version (initialisée à 0.1.0) et l'édition. Attention, pour cette dernière, il ne s'agit pas de l'année de création du paquet (eh non, Linux Mag ne recycle pas des articles de 2021 en 2023...). En fait, il s'agit de l'édition de Rust. Sauf circonstances exceptionnelles, vous ne devriez pas à avoir à modifier ce paramètre.

Dans l'esprit de Rust, la section s'achève avec une indication de la documentation qui précise et explique toutes les entrées qu'il est possible d'ajouter dans ce fichier. Suit la section [dependencies], vide pour le moment.

Passons maintenant aux fichiers main.rs et lib.rs. Ils sont sensiblement différents. Voyons tout d'abord main.rs :

fn main() {
    println!("Hello, world!");
}

J'avais dit que je ne le ferais pas ? Pardon, c'est bien malgré moi... Le fichier lib.rs est plus intéressant :

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        let result = 2 + 2;
        assert_eq!(result, 4);
    }
}

Il ne contient encore rien de fonctionnel qui puisse être utilisé par d'autres librairies ou applications, mais il fournit pourtant un squelette intéressant, à savoir une architecture minimale pour pouvoir effectuer des tests automatisés, accompagné d'un exemple de test vérifiant que deux plus deux égale quatre.

Remarquons au passage la façon dont les tests sont mis en place au sein de la librairie : une fonction de test (ici, it_works()) est précédée d'un attribut #[test] qui permet de l'identifier comme telle. Elle est placée au sein d'un bloc de code qui commence avec la déclaration mod tests. L'instruction mod permet d'identifier un module au sein du projet, ici, un module appelé tests. Bien qu'il serait idiot de le changer ici, ce nom n'est pas fonctionnel et notre module de tests pourrait très bien s'appeler pipo. C'est l'attribut qui le précède, #[cfg(test)] qui permet à Cargo de savoir que le module contient des tests.

Notons en passant que les modules aident à structurer le code au sein d'une crate et que les éléments qu'ils contiennent s'adressent en utilisant l'opérateur ::, tout comme les éléments d'une librairie :

mod my_mod {
    pub fn function() {
        println!("called `my_mod::function()`");
    }
}
fn main() {
    function();
    my_mod::function();
}

2. Compilation

Et si nous essayions déjà de compiler ce que nous avons là ? Cargo réalise cela à l'aide de la sous-commande build :

$ cd mogul
$ cargo build
   Compiling mogul v0.1.0 (/tmp/mogul)
    Finished dev [unoptimized + debuginfo] target(s) in 0.61s
$ tree .
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── target
    ├── CACHEDIR.TAG
    └── debug
        ├── build
        ├── deps
        │   ├── libmogul-b1d619082539333f.rlib
        │   ├── libmogul-b1d619082539333f.rmeta
        │   └── mogul-b1d619082539333f.d
        ├── examples
        ├── incremental
        │   └── mogul-1rfy2zocevqr8
        │       ├── s-gd68l85de2-9g4wxs-36n8770g7qutw
        │       │   ├── dep-graph.bin
        │       │   ├── h442n68pyjm3mn9.o
        │       │   ├── query-cache.bin
        │       │   └── work-products.bin
        │       └── s-gd68l85de2-9g4wxs.lock
        ├── libmogul.d
        └── libmogul.rlib

Nous n'allons pas examiner tous les fichiers créés, mais seulement l'arborescence produite. On se rend compte que, bien que notre projet soit encore quasiment vide, Cargo ne chôme pas. Si vous vous livrez au même examen avec le projet mogul-cli, vous remarquerez de petites différences, mais le résultat est globalement le même.

Remarquons tout d'abord l'apparition du fichier Cargo.lock, dont nous avons déjà parlé. Il reprend les informations du Cargo.toml, dans un format un peu différent et n'est pas destiné à être modifié manuellement, uniquement par Cargo.

L'essentiel a été ajouté dans le dossier target et son sous-dossier debug. C'est dans celui-ci que nous trouverons les binaires (libmogul.rlib pour la librairie, et mogul-cli pour l'application). D'ailleurs, une simple commande permet de tester tout de suite le bon fonctionnement de l'application :

$ cd ../mogul-cli/target/debug/
$ ./mogul-cli
Hello, world!

Cargo compile selon plusieurs profils, debug par défaut, un dossier pour chaque profil étant alors créé dans le dossier target. Avec debug, la compilation est plus rapide, mais le résultat n'est pas optimisé par le compilateur. Pour obtenir le résultat optimisé, il faut ajouter l'option --release : le dossier correspondant apparaît dans target, avec la même structure que pour debug.

Je ne résiste pas à vous parler de l'option --target (rien à voir avec le dossier) qui permet de compiler pour une plateforme différente que celle sur laquelle vous travaillez (cross-compilation). La commande rustc --print target-list permet de lister les plateformes possibles, et il y en a pas moins de 186 ! Toutes ces cibles ne sont pas présentement installées dans notre environnement de travail, il faut les installer.

Imaginons que je veuille compiler ma librairie en WebAssembly pour pouvoir l'exécuter dans un navigateur web. Il me faut d'abord installer cette cible :

$ rustup target add wasm32-wasi
info: downloading component 'rust-std' for 'wasm32-wasi'
info: installing component 'rust-std' for 'wasm32-wasi'
17.0 MiB / 17.0 MiB (100 %)   9.3 MiB/s in 1s ETA: 0s

Ensuite, je suis en mesure de compiler vers cette cible :

$ cargo build --target wasm32-wasi --release
   Compiling mogul-cli v0.1.0 (/tmp/mogul-cli)
    Finished release [optimized] target(s) in 0.20s

Dès lors, le dossier target se voit augmenté du dossier wasm32-wasi, qui reprendra la même architecture avec un dossier release (ou debug, si vous n'avez pas précisé l'option --release).

3. Gestion des dépendances

L'ajout d'une dépendance à un projet est une chose aisée. Il suffit d'éditer le fichier Cargo.toml et d'ajouter la crate désirée dans la section [dependencies]. Dans le cas de Mogul, nous avons deux dépendances : les librairies Uuid et Serde, la première pour générer des identifiants universels uniques et la seconde pour pouvoir sérialiser et dé-sérialiser des MogulHandlers.

Pour information, l'ensemble des paquets disponibles librement est mis à disposition par l'intermédiaire du dépôt https://crates.io. Sur le site, un moteur de recherche et un classement par catégories vous rendront service pour trouver la librairie qui vous manque. Pour chacune d'elles, une page complète d'information vous renseignera plus en détail, avec le numéro de la dernière version, des liens utiles vers la documentation et le dépôt. Notons que sur le même principe, le site https://docs.rs centralise la documentation de tous les paquets disponibles.

Si vous lisez un peu trop vite la documentation de Cargo, vous pourriez croire que nous aurions vite réglé nos problèmes de dépendances avec ce bloc :

[dependencies]
uuid = "1.1.2"
serde = "1.0.144"serde_json = "1.0"

Cela fonctionnera... mais pas complètement. En effet, dans un souci de performance, toutes les fonctionnalités des crates ne sont pas activées par défaut et il est nécessaire de préciser celles dont on a besoin à l'aide de la directive [features]. Notre bloc devient alors :

[dependencies.uuid]
version = "1.1.2"
features = [
    "v4",
    "serde"
]
[dependencies.serde]
version = "1.0.144"
features = [
    "derive"
]
[dependencies.serde_json]
version = "1.0"

De uuid, nous avons besoin de deux fonctionnalités v4 pour pouvoir générer des UUID aléatoires et serde pour pouvoir sérialiser/dé-sérialiser avec serde les UUID générés. De serde, nous avons besoin de la fonctionnalité derive pour pouvoir créer des structures dérivées de Serde et leur permettre de se sérialiser/dé-sérialiser.

Cargo autorise d'autres formats. Ainsi, le bloc suivant est équivalent au précédent :

[dependencies]
uuid = { version = "1.1.2", features = ["serde", "v4"] }
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0"

À la prochaine compilation, Cargo générera un fichier Cargo.lock qui définira les versions de chaque librairie utilisée par notre projet.

Vous aurez peut-être remarqué que nous avons indiqué un numéro de version précis et unique pour chacune de nos dépendances. Cela signifie-t-il que nous avons verrouillé la version ? Ne peut-on pas indiquer plus de souplesse ? Oui, il est possible d'autoriser plus de souplesse, mais dans la plupart des cas, cela n'est pas nécessaire. En effet, Cargo interprète ce numéro comme un minimum à l'intérieur d'une version majeure. Ainsi, le jour où sort la version 1.2.0 de la librairie Uuid, la commande cargo update générera un nouveau cargo.lock, qui indiquera cette nouvelle version, et non celle mentionnée dans notre fichier Cargo.toml. Par contre, si un jour Uuid passait à la version 2.0.0, celle-ci ne pourrait être utilisée pour notre projet.

Nos dépendances n'ont-elles pas elles-mêmes des dépendances ? Sans doute. La commande cargo tree permet de les connaître. Pour Mogul, cela nous donne :

mogul v0.1.0 (/chemin/vers/mogul)
├── serde v1.0.144
│   └── serde_derive v1.0.144 (proc-macro)
│       ├── proc-macro2 v1.0.43
│       │   └── unicode-ident v1.0.3
│       ├── quote v1.0.21
│       │   └── proc-macro2 v1.0.43 (*)
│       └── syn v1.0.99
│           ├── proc-macro2 v1.0.43 (*)
│           ├── quote v1.0.21 (*)
│           └── unicode-ident v1.0.3
├── serde_json v1.0.85
│   ├── itoa v1.0.3
│   ├── ryu v1.0.11
│   └── serde v1.0.144 (*)
└── uuid v1.1.2
    ├── getrandom v0.2.7
    │   ├── cfg-if v1.0.0
    │   └── libc v0.2.132
    └── serde v1.0.144 (*)

Cette information, plus digeste que la lecture du fichier cargo.lock, peut être utile à plus d'un titre. La première qui vient à l'esprit concerne la sécurité : il est bon de connaître les librairies utilisées pour réagir quand une faille de sécurité a été identifiée, et savoir quand lancer un cargo update salvateur. Et si vous souhaitez approfondir la question, exécutez cargo vendor : les sources de toutes vos dépendances seront téléchargées localement et vous aurez ainsi tout le loisir de faire votre propre audit, si le cœur vous en dit.

4. Tests et documentation

Ici, nous n'allons pas tant nous intéresser à la commande Cargo (cargo test) qui permet de réaliser les tests qu'à la façon de les implémenter avec Rust. Le sujet est vaste, sans aucun doute, mais l'ambition ici n'est pas de l'épuiser, plutôt de donner un aperçu des facilités que Rust propose pour les réaliser.

Il est possible de le faire de deux façons : soit sous forme de code, soit sous forme de commentaire.

4.1 Le module tests

Rust permet naturellement d'écrire les tests dans des fichiers séparés et, lorsqu'un projet prend de l'ampleur, cela devient rapidement une nécessité pour garder un code lisible. Mais si votre projet reste modeste, comme c'est le cas pour notre librairie Mogul qui ne devrait pas prendre beaucoup d'ampleur, il peut être agréable d'avoir les tests à même le code qu'ils doivent valider.

Reprenons quelques lignes de code de notre librairie :

017: #[cfg(test)]
018: mod tests {
019:     #[test]
020:     fn it_works() {
021:         use uuid::Uuid;
022:         use crate::MogulError;
023:         use crate::MogulHandler;
024:         use crate::mogul_allow_update;
025: 
026:         let original = MogulHandler { uuid: Uuid::new_v4(), state: Uuid::new_v4(), history: vec![Uuid::new_v4(),Uuid::new_v4()]};
027:         let challenger_not_same = MogulHandler { uuid: Uuid::new_v4(), state: Uuid::new_v4(), history: vec![Uuid::new_v4(),Uuid::new_v4()]};
028:         assert_eq!(mogul_allow_update(&original, &challenger_not_same), Err(MogulError::NotVersionsOfTheSameObject));
029:     }
030: }

Ici est défini le module tests (ligne 18), qui n'est compilé que lorsque la configuration de test est définie (ligne 17). L'attribut #[test] à la ligne 19 indique que la fonction qui suit est un test à exécuter. Celle-ci débute avec la déclaration de tous les éléments extérieurs nécessaires à son exécution et nous y trouvons bien des éléments que nous avons définis dans notre librairie. Ne pourrait-on s'en passer ? Bien que le test en fasse partie, il se déroule à l'extérieur d'elle, il est un programme purement client de notre librairie. Rappelons que les tests peuvent aussi servir de documentation en décrivant la façon d'utiliser les éléments mis à disposition.

À la ligne 28, nous trouvons la macro assert_eq! qui vérifie l'égalité de ses deux arguments. Il s'agit d'une assertion, concept habituel des tests unitaires qui vérifie une proposition. Rust fournit également les macros assert! et assert_ne! permettant respectivement de vérifier si une expression est vraie et si deux expressions renvoient des résultats différents. Un test est donc réussi lorsque l'ensemble des assertions qu'il contient sont vraies.

4.2 Tests documentaires

Mais si les tests ont une valeur documentaire, alors pourquoi ne pourrions-nous pas les inclure dans les commentaires des documentations ? Voilà une bonne idée que Rust a intégrée d'emblée.

Comme nous l'avons vu dans notre article d'introduction, Rust accepte des commentaires documentaires suivant les marqueurs /// ou /**, selon qu'il s'agisse d'une ligne ou d'un bloc. Le contenu de ces documentations adopte le format Markdown, ce qui permettra des affichages élégants sur docs.rs. Markdown prévoit que l'on puisse délimiter des blocs de code à l'aide de trois apostrophes ```. La bonne idée est alors d'utiliser ces blocs non seulement pour les mettre en forme correctement dans la documentation publiée, mais aussi de les exécuter au moment des tests automatiques. On fait ainsi d'une pierre deux coups : on élabore les tests et la documentation en même temps et les tests permettent également de valider la documentation !

Ainsi, le bloc :

083: /// Decide if an update should occurs
084: /// 
085: /// ```
086: /// use uuid::Uuid;
[...]
102: /// assert_eq!(original.get_uuid(), original.get_uuid());
103: /// ```
104: #[allow(dead_code)]
105: pub fn mogul_allow_update(original:&MogulHandler, challenger:&MogulHandler) -> Result<(), MogulError> {

documente-t-il la fonction mogul_allow_updateen, donnant un exemple de son fonctionnement tout en permettant à Cargo de vérifier un certain nombre d'assertions :

$ cargo test
   Compiling mogul v0.1.0 (/chemin/vers/mogul)
    Finished test [unoptimized + debuginfo] target(s) in 0.63s
     Running unittests src/lib.rs (target/debug/deps/mogul-7d6509701a8ec3e3)
 
running 1 test
test tests::it_works ... ok
 
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 
   Doc-tests mogul
 
running 2 tests
test src/lib.rs - mogul_merge (line 127) ... ok
test src/lib.rs - mogul_allow_update (line 83) ... ok
 
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.58s

On retrouve encore une fois l'esprit de Rust qui incite le développeur à adopter de bonnes pratiques en lui rendant service.

Mais pourquoi préférer les trois slashes aux deux astérisques ? Cela ne tient pas tant à Rust qu'aux outils utilisés pour le développement. Certains IDE parmi les plus populaires, ayant identifié que les commentaires peuvent contenir du code, opèrent une coloration syntaxique. Du coup, l'utilisation de /**, qui ne marque pas le début de chaque ligne, peut rendre le code un peu confus à lire, induisant une confusion entre le code principal et celui des tests documentaires. L'utilisation de /// à chaque ligne permet un rendu plus clair.

5. Publication

Mettre à disposition son code est toujours une étape engageante pour un développeur de logiciels libres. Comme nous l'avons dit, avec Rust, les paquets sont centralisés sur https://crates.io. Voyons comment y publier notre librairie.

Mais attention ! Une fois rendu public, votre code ne pourra plus être retiré. Vous pourrez tout au plus utiliser la commande cargo yank pour déprécier votre crate, ce qui interdira son utilisation à tous les projets à venir et les supprimera des projets indexés, mais le code sera toujours là et accessible aux projets qui l'utilisaient auparavant.

5.1 Préliminaires à toute publication

Pour commencer, il vous faudra un compte GitHub. On peut trouver cela contraignant (après tout, si GitHub héberge bien des projets libres, il ne l'est pas en lui-même... sans compter qu'il a été racheté par Microsoft, ce qui n'est pas sans poser problème aux esprits critiques, dont je suis !). Mais la documentation précise « required for now » (« obligatoire pour le moment »), ce qui laisse à penser que cette contrainte pourrait évoluer à l'avenir.

Sur la page d'accueil de https://crates.io, identifiez-vous à l'aide de votre compte GitHub et autorisez rust-lang à accéder à votre compte en lecture seule. Cela fait, rendez-vous à la page « Account settings » pour y générer un nouveau token, indispensable pour pouvoir publier sur crates.io. Copiez-le soigneusement. Lancez la commande cargo login et copiez le token lorsque vous y êtes invité :

$ cargo login
please paste the API Token found on https://crates.io/me below
VotreTokenIci
       Login token for `crates.io` saved

Gardez bien votre token secret, sans quoi on pourrait usurper votre identité et publier des paquets malfaisants en votre nom.

5.2 Préparer sa crate

Avant de livrer votre code au monde entier, il convient de faire en sorte qu'il soit présentable et surtout repérable. Je ne doute pas que votre code soit le plus beau du monde, mais il convient de compléter un peu cargo.toml pour qu'il trouve bien sa place sur crates.io. En effet, certaines informations y sont obligatoires et d'autres fortement recommandées, toutes prenant place dans la section [package].

Pour les obligatoires, nous avons :

  • licence ou licence-file : une indication de licence ou un fichier ; dans le cas d'une librairie, "MIT OR Apache-2.0" est préférable pour ne pas limiter l'interopérabilité avec le reste de l'écosystème ;
  • description : quelques mots expliquant l'utilité de votre crate ;
  • homepage : une URL indiquant le site web présentant votre projet ;
  • repository : une URL permettant de consulter le code source ;

Pour les recommandés, il nous reste :

  • keywords : quelques mots clés pour aider à trouver votre projet, maximum cinq ;
  • categories : dans quelles catégories classer votre crate ; à choisir dans une liste [2], en respectant scrupuleusement la syntaxe ; maximum cinq.

Si ce n'est déjà fait, il faut également ajouter un fichier README.md à votre projet décrivant plus en détail son intérêt et son utilisation.

Voilà à quoi ressemble notre section [package] au final :

[package]
name = "mogul"
version = "0.1.1"
authors = ["Stéphane Mourey"]
license = "MIT OR Apache-2.0"
description = "Agnosticly helps to manage concurrent versions of things"
homepage = "https://mogul-project.stephanemourey.fr"
repository = "https://mogul-sources.stephanemourey.fr/mogul"
keywords = ["versions", "concurrent", "conflict", "manage"]
categories = ["concurrency", "data-structures", "date-and-time"]
edition = "2021"

5.3 Publier !

Bon, ben, y'a plus qu'à...

$ cargo publish
    Updating crates.io index
   Packaging mogul v0.1.0 (/chemin/vers/mogul)
   Verifying mogul v0.1.0 (/chemin/vers/mogul)
    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
    Blocking waiting for file lock on package cache
   Compiling proc-macro2 v1.0.43
   Compiling quote v1.0.21
   Compiling unicode-ident v1.0.3
   Compiling syn v1.0.99
   Compiling serde_derive v1.0.144
   Compiling libc v0.2.132
   Compiling serde v1.0.144
   Compiling cfg-if v1.0.0
   Compiling serde_json v1.0.85
   Compiling itoa v1.0.3
   Compiling ryu v1.0.11
   Compiling getrandom v0.2.7
   Compiling uuid v1.1.2
   Compiling mogul v0.1.0 (/chemin/vers/mogul/target/package/mogul-0.1.0)
    Finished dev [unoptimized + debuginfo] target(s) in 26.34s
   Uploading mogul v0.1.0 (/chemin/vers/mogul)

Dès lors, le paquet est indexé, avec tout ce qu'il faut : on le trouve immédiatement sur crates.io [3]. La documentation n'est pas générée immédiatement, il y a une file d'attente... mais elle est bien vite disponible sur docs.rs [4] ! Bon, je vous laisse, il est temps d'ouvrir le champagne...

Conclusion

Nous retrouvons avec Cargo l'esprit caractéristique de Rust : exigence de qualité, bonnes pratiques facilitées, efficacité, concision, et surtout, un outil qui se met au service du développeur. Il y ajoute un esprit de communauté en facilitant grandement la distribution des paquets et de leur documentation. Alors, on embarque ?

Références

[1] https://doc.rust-lang.org/cargo/faq.html#why-do-binaries-have-cargolock-in-version-control-but-not-libraries

[2] https://crates.io/category_slugs

[3] https://crates.io/crates/mogul

[4] https://docs.rs/mogul/



Article rédigé par

Par le(s) même(s) auteur(s)

ntfy.sh : installez un service de notification pour suivre les événements de votre SI

Magazine
Marque
Linux Pratique
Numéro
144
Mois de parution
juillet 2024
Spécialité(s)
Résumé

ntfy.sh (prononcez « notify ») est un service en ligne vous permettant d’envoyer et de recevoir des notifications avec une facilité déconcertante. Comme il s’agit d’un logiciel libre, aussi bien en ce qui concerne le serveur que les clients, il n’y a vraiment pas de raison de se priver !

FrankenPHP : vers une nouvelle génération de serveurs PHP ?

Magazine
Marque
GNU/Linux Magazine
Numéro
268
Mois de parution
mars 2024
Spécialité(s)
Résumé

C'est bien connu, PHP est lent. Si au fil des versions, il a fait énormément de progrès, son principe de fonctionnement originel, celui d'un langage interprété nécessitant la relecture de tous les fichiers impliqués à chaque requête, pose une limite indépassable. Vraiment ? Et si le serveur conservait toute l'application en mémoire à tout moment ? Voilà ce que vous propose FrankenPHP.

Les derniers articles Premiums

Les derniers articles Premium

PostgreSQL au centre de votre SI avec PostgREST

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Dans un système d’information, il devient de plus en plus important d’avoir la possibilité d’échanger des données entre applications. Ce passage au stade de l’interopérabilité est généralement confié à des services web autorisant la mise en œuvre d’un couplage faible entre composants. C’est justement ce que permet de faire PostgREST pour les bases de données PostgreSQL.

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 65 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous