C’est parti !

 

Enfin ! C’est finalement enfin le vrai commencement de cette petite série entièrement dédiée à l’apprentissage de Rust.

Aujourd’hui, plus question de parler d’autres langages. Je vais uniquement évoquer Rust !

Et pour celles et ceux qui rejoindraient la série en cours de route, vous pouvez toujours consulter le premier article d’introduction pour découvrir mes motivations et le but final de ce projet.

C’est fait ? Tout le monde est à jour ? Alors c’est parti !

 

Première étape : installer Rust… (oui, cela semble logique)

 

Aller hop, pas le temps de niaiser, comme l’aurait sans doute dit un de nos cousins outre-atlantique. Je vais sur le site officiel, télécharge l’exe d’installation rustup-init pour windows, et je le lance.

Dès le début, tout est incroyablement facile.

Rustup-init s’occupe de modifier les variables d’environnements afin de rajouter les raccourcis des commandes, et il installe toutes les dépendances sans broncher, automatiquement.

A peine 10 minutes plus tard – le temps de télécharger et d’installer les dépendances – tout est en règle. Je peux commencer à programmer en Rust !

Ça parait bête d’écrire ça, en 2018, mais quand même : c’est beau quand c’est bien fait !

 

Deuxième étape : cargo, l’outil indispensable

 

C’est parti, je suis lancé !

Je créé donc sans plus attendre un nouveau dossier qui contiendra mon projet, et je lance la fameuse commande :

cargo new bsc --bin

 

Exactement comme indiqué sur le tutoriel officiel.

Bsc étant le nom du projet, et le flag bin, pour indiquer que je souhaite créer un programme exécutable, et non une library.

 

Hop hop hop. On va un peu vite en besogne non ? C’est quoi cargo tout d’abord ?

 

Cargo, c’est un peu THE CLI de Rust. C’est la commande qui permet de tout faire.

En fait, on peut très bien compiler du Rust en utilisant uniquement la commande rustc, qui s’utilise grosso-modo comme gcc en C/C++.

Mais pour des gros projets, c’est tout de suite plus complexe, car il faut spécifier les fichiers dans l’ordre d’appels des fonctions… (c’est ce qu’on appelle le linking, et c’est pour ça qu’on a inventé les fichiers makefile pour le C/C++ par exemple), sans oublier qu’il y a beaucoup de fichiers, donc la commande fait vite plusieurs lignes. Bref, ce n’est pas pratique…

Heureusement, cargo est la ! cargo s’occupe de tout, tout seul, via un panel de sous-commandes aussi simples qu’intuitives !

En fait, cargo, c’est une commande assez similaire à ce que je souhaite faire, avec bsc. Elle permet de générer le squelette d’un projet Rust, de le build, et de le run. Elle sert également de package manager…

Bref, quand on développe en Rust, il faut croire que c’est la commande miracle. On l’utilise constamment !

Mais revenons à nos moutons, et regardons ensemble ce que m’a généré la commande précédente, ci-dessus :

tree .

.
├───Cargo.toml
├───.gitignore
├───.git
├───src
    ├───main.rs
└───target

 

Cargo.lock et Cargo.toml sont des fichiers permettant la gestion des paquets, pour le package manager.

C’est exactement la même chose que les fichiers packages.json et packages.lock en node.js, par exemple, au final.

Cargo.toml s’occupe de lister toutes les dépendances du projets, tandis que Cargo.lock permet de verrouiller les versions des dépendances, afin de s’assurer que tout le monde ait bien exactement le même code (dans le cas où plusieurs développeurs travailleraient ensemble sur un même projet).

Le dossier src contient… vous l’aurez deviné : le code source (pour l’instant, il ne contient que le fichier principal avec la fonction main, ou plus simplement dit, l’entrée du programme).

Le dossier target quant a lui contiendra les dépendances du projet, ainsi que les exécutables (fichiers .exe sous windows) résultants des builds.

Pour finir, la commande cargo new initialise également automatiquement un répertoire git, et un fichier gitignore par défaut pour le Rust.

Finalement, rien de très compliqué ! Ça ressemble vraiment beaucoup à bscxx tout ça ! Haha

 

Troisième étape : la découverte des crates, et de clap

 

Cette fois-ci, c’est la bonne ! Je rentre enfin vraiment dans le « dur », et je commence a écrire du code en Rust pour poser la toute première brique de bsc !

Minute !

Je vais développer un programme de type CLI (Command Line Interface), et d’après mon expérience de programmeur, je suis sûr qu’il existe une library, ou un package (dans le cas de Rust) qui permet de faire ça proprement, et très simplement.

Si je veux apprendre le Rust, alors autant apprendre tout de suite à utiliser intelligemment le code d’un package existant, qui s’occupe déjà de régler le problème auquel je vais me confronter : à savoir la création d’une CLI avec un code propre et structuré.

Ni d’une, ni deux, je fonce sur google.

Bingo !

Un des résultats de ma recherche correspond exactement à ce que je veux (après avoir vu quelques exemples). Le petit nom du package en question ? clap.

Aller hop, je ne perds pas de temps, c’est parti ! Je lance directement une recherche supplémentaire pour comprendre comment installer ce package, et comment l’utiliser.

C’est à ce moment notamment que j’apprends le lexique spécifique de Rust.

Un package est désigné comme un crate dans le cas où le code source de ce dernier est externe au projet (et qu’il peut donc être installé en tant que dépendance externe du projet). Dans le cas de package dont le code source est non dissociable de celui du projet, ils appellent ça tout simplement un module.

La nuance semble pour l’instant fine, on verra par la suite ce que cela implique.

Pour en revenir aux crates, donc, le Rust dispose de son propre site officiel regroupant tous les packages crées par la communauté : crates.io.

Bref, c’est comme NPM pour nodejs, rien de nouveau.

Les fonctionnalités de recherche fonctionnent bien, et toutes les autres features « standard » sont de la partie : avis des utilisateurs, statistiques sur le nombre de téléchargement par crate, détails de versions, documentations, README…

Finalement, c’est tout ce que demande le peuple pour ce genre de site officiel d’un package manager. C’est parfait !

 

 

Maintenant que j’ai trouvé le bon crate pour résoudre mon problème, comment je fais pour l’installer et l’utiliser alors ?

 

Encore une fois, le maître mot est simplicité.

A la différence de NPM cela dit (pour reprendre la comparaison avec ce dernier), on ne tape pas une commande qui pourrait, par exemple, être « cargo add clap« .

Non, ici, on fait autrement.

Cela dit, c’est tout aussi simple, je vous assure. Il suffit de se mettre sous la section « [dependencies] » du fichier Cargo.toml pour ajouter le nom du crate, et sa version (qui respecte la norme classique semver) :

[package]
name = "bsc"
version = "0.1.0"
authors = ["Elkantor <victor.gallet@developing-stuff.com>"]

[dependencies]
clap = "2.32.0" // J'ajoute cette ligne en dessous de [dependencies] spécifiant que je veux utiliser le crate nommé clap, dans sa version 2.32.0

 

Et c’est tout !

A mon prochain appel de la commande :

cargo build // ou cargo run

le crate est automatiquement téléchargé dans sa version 2.32.0 et ajouté comme dépendance du projet.

 

Okay, mon projet dépend maintenant de ce crate, c’est bien mais… comment je fais pour l’utiliser concrètement ?

 

Encore une fois, rien de plus simple !

Pour utiliser n’importe lequel des crates dont dépend le projet, il suffit que j’ajoute cette instruction en haut du fichier Rust où je souhaite utiliser le crate en question :

extern crate clap; // Ici, en ajoutant cette ligne, je signale au compilateur que j'utilise les fonctions/structures/classes... du crate clap.

 

Cela fonctionne exactement de la même manière que les instructions de préprocesseurs include en C/C++, les import en Java, ou encore les require en JS.

A noter, tant que j’évoque les instructions prédéfinies du langage, qu’à l’instar du C++, le Rust possède également un système de namespaces. Cela s’utilise exactement de la même manière d’ailleurs, excepté que l’instruction using est remplacé par use en Rust.

Exemple d’utilisation de la structure Error disponible dans le namespace std::error (std signifiant, comme en C++, qu’il s’agit de la library standard) :

use std::error::Error; // Cette ligne me permet donc par la suite de remplacer "std::error::Error" simplement par "Error".

 

Petite note personnelle rapide : je suis très peu fan de l’utilisation de cette instruction. Par soucis de lisibilité du code,  j’ai en effet pris pour habitude de toujours écrire le namespace entier à chaque appel. Cela permet par exemple d’éviter les erreurs dues à l’éventualité où plusieurs structures/fonctions… de namespaces différents seraient nommés de la même manière. C’est un choix purement personnel, et bien entendu, chacun code comme il l’entend. Disons que c’est une habitude que j’ai pris. 😉

Bref, je me disperse. Revenons à nos moutons. Il me semble que je vous parlais du crate clap, non ?

Son utilisation est très simple. Cela dit, sa documentation n’est pas très claire, ni très fournie… Heureusement qu’il y a plein d’exemples sur le github du projet !

Voici donc mon tout premier bout de code en Rust, situé dans la fonction d’entrée du programme (fn main), dans mon fichier main.rs (vous vous souvenez ? Ce fichier est automatiquement généré par cargo lors de la création du projet, dans le dossier src).

Et pour rappel, le but jusqu’ici est de pouvoir obtenir très rapidement une CLI :

fn main() {
    let matches = clap::App::new("bsc")
        .version("0.1.0")
        .author("Victor Gallet <victor.gallet@developing-stuff.com>")
        .about("clone of bscxx (a package manager for C++), but for C language, made in Rust for learning purpose")
        .subcommand(clap::SubCommand::with_name("create")
            .about("create a new project / module")
            .arg(clap::Arg::with_name("PROJECT_NAME")
                .help("the name of the project / module")
                .required(true)
                .takes_value(true)
                .index(1)
            )
        )  
        .get_matches();

    match matches.subcommand() {
        ("create", Some(create_matches)) => create_project("./", create_matches.value_of("PROJECT_NAME").unwrap()),
        ("", None) => println!("No subcommand was used"),
        _ => unreachable!(),
    }
}

 

Hmm, c’est pas très clair… Des explications ?

Mais bien entendu, ne vous en faites pas, je vais tout vous expliquer ligne par ligne.

let matches = clap::App::new("bsc");

 

Ici, je créé une variable nommée « matches », qui n’est autre qu’une structure de type App, définie dans le code du crate clap (c’est notamment écrit dans la documentation).

Hmm… Mouais… Cela dit, ce n’est pas tout à fait « vraiment » ce que cela stocke dans la variable matches au final (car vous l’avez vu, il n’y a pas de point virgule à la fin de la ligne dans le bloc de code plus haut, signifiant que l’instruction n’est pas terminée).

C’est d’ailleurs pour cela que cette variable se nomme matches, et non app. Mais j’y reviendrai par la suite, promis.

Je souhaite pour le moment ne perdre personne en cours de route, donc je vais supposer que ça instancie bien une structure de type App.

Vous l’aurez sans doute vite compris, en Rust, comme en JS, les variables sont créées à l’aide du mot clef let. Pas besoin d’ajouter le type de variable, ici, le compilateur va regarder la partie droite de l’instruction (clap::App::new(« bsc »)), et il va automatiquement assigner l’instanciation d’une nouvelle structure sur le tas (heap).

Bien entendu, je peux spécifier manuellement le type de variable que je souhaite déclarer. La ligne ci-dessous fournie par exemple exactement le même résultat.

let matches: clap::App = clap::App::new("bsc"); // Cela donne le même résultat que la ligne précédente.

 

Ici, c’est inutile. Mais imaginons que je souhaite déclarer un nombre de -128 a 127(8 bits), spécifiquement, par exemple, ça a son importance, car le compilateur stockera n’importe quel nombre sur 32 bits, par défaut, si on ne spécifie pas le type.

let i = 1; // Par défaut en Rust, le compilateur considère que le type de i est un i32, soit un nombre entier stocké sur 32 bits, pouvant aller de -2147483648 a 2147483647.
let i: i32 = 1; // Cette ligne est donc identique à la ligne précédente.
let i: i8 = 1; // Ici, je spécifie que la variable i est un entier stocké sur 8 bits. Donc sa valeur peut aller de -128 a 127.
let i: u8 = 1; // Et ici, je déclare i en tant qu'entier non signé sur 8 bits. Donc sa valeur va de 0 a 255. 

 

Pour ceux qui viendraient du C++, sachez que l’appel du new en Rust, fonctionne exactement de la même façon que le new en C++. Il permet d’appeler automatiquement le constructeur de la structure, et de l’instancier sur le tas.

Cela revient à faire un malloc en C.

Si je souhaitais que la variable soit stockée en mémoire sur la pile (stack), j’aurais pu tout simplement écrire :

let matches = clap::App {
    // en initialisant tous les champs de la structure App
};

 

Bref, dans tous les cas, toi qui lis ces lignes, sache que, si tu n’as que peu (ou pas) d’expérience en C/C++ et en gestion de la mémoire, et que tu n’es pas habitué(e) à ces notions de pile/tas, je peux revenir dessus dans un prochain article (il suffit de me le notifier dans les commentaires, en bas de la page). 😉

Au final, le Rust, c’est un peu un mélange entre le JS (et globalement, tous les nouveaux langages haut niveau), et le C++.

Aller, je continue !

Que fait la ligne ci-dessous ?

.version("0.1.0")

 

C’est tout simple. La structure App du crate clap possède une méthode version permettant de remplir son champ… version…

Logique !

Petite chose à remarquer, j’aurais pu écrire la même chose sur deux lignes :

let matches = clap::App::new("bsc");
matches.version("0.1.0");

 

Cela revient exactement au même.

Mais comme on peut enchainer les fonctions, lors de l’instanciation d’une structure/classe en Rust, et que c’est une norme dans ce langage  de faire comma ça, j’ai préféré me forcer à coder de la même manière que la majorité des développeurs Rust (ils se font appelés « rustaceans » au passage).

Ah, et tant que j’évoque les normes, sachez que le compilateur vous renverra des warnings si vous ne respectez pas les conventions de nommage officielles du Rust.

Pas bête comme système pour que tout le monde écrive du code lisible, et similaire, non ?

Bref, je continue. De la même manière que pour la version, les lignes ci-dessous permettent de remplir logiquement les champs author et about de la structure :

.author("Victor Gallet <victor.gallet@developing-stuff.com>")
.about("clone of bscxx (a package manager for C++), but for C language, made in Rust for learning purpose")

 

Rentrons désormais dans le vif du sujet !

Pour essayer d’expliquer plus « simplement » possible les lignes suivantes, je vais commencer par vous décrire ce que j’ai fait en premier lieu, pour comprendre le fonctionnement de clap.

Aller hop, petit retour dans le passé !

Je viens de découvrir clap. Je regarde donc sa documentation, et j’écris le code ci-dessous, en simplifiant au maximum pour comprendre son fonctionnement :

extern crate clap;

fn main() {
    let matches = clap::App::new("bsc")
        .version("0.1.0")
        .author("Victor Gallet <victor.gallet@developing-stuff.com>")
        .about("clone of bscxx (a package manager for C++), but for C language, made in Rust for learning purpose")
        .get_matches();
}

 

Et voila le résultat que j’obtiens lorsque je lance le programme :


 

Euh… C’est a dire qu’il n’y a rien en fait… Ou alors j’ai un problème de vision ?

 

 

Ah mais oui bien sur ! Effectivement, mon programme ne fait rien, et c’est tout a fait normal !

J’ai oublié qu’il s’agit d’une CLI (Command Line Interface), et comme tout bonne CLI, elle ne fait rien sans arguments, et/ou sans flags.

Je relance donc le programme avec l’option -h pour afficher l’aide, juste pour tester.

Au passage, je découvre que je peux directement compiler et lancer le programme en une seule commande, tout en ajoutant des paramètres, à l’aide de la commande cargo run — <parametre>.

Pratique non ?

$ cargo run -- -h
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target\debug\bsc.exe -h`

bsc 0.1.0
Victor Gallet <victor.gallet@developing-stuff.com>
clone of bscxx (a package manager for C++), for C language, made in Rust for learning purpose

USAGE:
    bsc.exe

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

 

Bingo ! C’est déjà mieux !

Au final, en moins de 5 minutes, et avec seulement quelques lignes de code, en utilisant le crate clap, j’ai déjà la base d’une CLI, avec les flags -h (ou –help) et -v (ou –version) pris en charge.

Pas mal.

Je vois bien mes champs remplis précédemment. Tout est bon !

 

Okay, c’est sympa, mais comment je fais maintenant pour ajouter des commandes, des flags… ?

 

Hop, retour dans le présent !

Je reviens au bloc de code précédant pour vous expliquer (enfin !) la suite. Pour rappel, il s’agit de ces lignes :

... // code avant
.subcommand(clap::SubCommand::with_name("create") 
    .about("create a new project / module") 
    .arg(clap::Arg::with_name("PROJECT_NAME") 
    .help("the name of the project / module") 
    .required(true) 
    .takes_value(true) 
    .index(1))) 
.get_matches(); 

match matches.subcommand() { 
    ("create", Some(create_matches)) => create_project("./", create_matches.value_of("PROJECT_NAME").unwrap()), 
    ("", None) => println!("No subcommand was used"), 
    _ => unreachable!(), 
}
... // code après (fin de la fonction main)

 

Si vous vous souvenez de ce que j’écrivais dans l’article d’introduction de cette série, l’outil CLI que je vais développer en Rust contient plusieurs fonctionnalités, dont la création d’un projet (permettant de générer le squelette, exactement comme la commande cargo new).

Il faut donc que j’ajoute des paramètres à mon outil…

En gros, le but est d’avoir au final, une commande de ce type :

bsc create PROJECT_NAME

 

En cherchant dans les exemples de clap, j’en déduis qu’il me faut une subcommand (ou sous-commande, en bon français).

Ma sous-commande étant create.

Chaque sous-commande peut contenir  à son tour des flags, des options, et des arguments.

Au final, les lignes ci-dessous permettent tout simplement d’ajouter une sous-commande (create), lui associer un text about qui est affiché via le flag d’aide (-h ou –help), et pour finir, de la lier à un argument (PROJECT_NAME, le nom du projet).

.subcommand(clap::SubCommand::with_name("create") 
    .about("create a new project / module")
    .arg(clap::Arg::with_name("PROJECT_NAME") 
        .help("the name of the project / module") 
        .required(true) // l'argument est obligatoire
        .takes_value(true) // l'argument ne peut pas etre nul
        .index(1))) // il s'agit de la premiere chaine de caractere. Par exemple, si on rentre "bsc create test 2", PROJECT_NAME aura pour valeur "test"

 

Aller, c’est parti, testons tout ça !

Après compilation, et en utilisant la commande cargo run — create -h, on obtient :

$ cargo run -- create -h

Finished dev [unoptimized + debuginfo] target(s) in 1.06s
     Running `target\debug\bsc.exe create -h`
bsc.exe-create
create a new project / module

USAGE:
    bsc.exe create <PROJECT_NAME>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <PROJECT_NAME>    the name of the project / module

 

Impeccable !

On peut également tester que le paramètre de cette sous-commande est bien obligatoire, et qu’il doit être non nul :

$ cargo run -- create 
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\bsc.exe create`
error: The following required arguments were not provided:
    <PROJECT_NAME>

USAGE:
    bsc.exe create <PROJECT_NAME>

For more information try --help

 

Parfait ! Tout fonctionne correctement !

Mais ne partez pas encore, il faut absolument que je vous explique les lignes suivantes !

Plus haut, dans cet article, j’écrivais que, pour que mes explications ne soient pas confuses, le contenu de la variable « let matches = clap::App(« bsc)… » était une instance de la structure App du crate clap. Vous vous souvenez ?

Et bien c’est faux…

Effectivement, il y a un appel a la fonction get_matches()à la toute fin du bloc de code :

let matches = clap::App::new("bsc") 
    .version("0.1.0") 
    .author("Victor Gallet <victor.gallet@developing-stuff.com>") 
    .about("clone of bscxx (a package manager for C++), but for C language, made in Rust for learning purpose") 
    .subcommand(clap::SubCommand::with_name("create") 
        .about("create a new project / module") 
        .arg(clap::Arg::with_name("PROJECT_NAME") 
            .help("the name of the project / module") 
            .required(true) 
            .takes_value(true) 
            .index(1) 
        ) 
    ) 
    .get_matches(); // Je parle de cette ligne là

 

Et oui, j’instancie bien une structure App au tout début, mais au final, ce que je stock dans la variable matches, c’est une structure de type ArgMatches, du crate clap.

Rien de bien compliqué, je vous assure.

En fait, cette structure permet simplement de récupérer des informations concernant les arguments, les sous-commandes et les flags, de notre programme.

Et oui, c’est bien beau d’avoir créé notre sous-commande create, mais encore faudrait-il :

  1. savoir si l’utilisateur a effectivement lancé cette sous-commande
  2. si oui, pouvoir récupérer le nom du projet afin de générer le squelette

C’est exactement le but des dernières lignes :

match matches.subcommand() { 
    ("create", Some(create_matches)) => create_project("./", create_matches.value_of("PROJECT_NAME").unwrap()), 
    ("", None) => println!("No subcommand was used"), 
    _ => unreachable!(), 
}

 

Petite particularité à noter. Comme vous l’aurez constaté, en Rust, le mot clef switch est remplacé par match.

Les lignes suivantes signifient donc qu’en fonction du résultat de matches.subcommand(), on va effectuer différentes actions.

Dans mon cas, il n’y en a que deux possibilités, pour le moment :

  • un appel de la sous-commande create
  • le cas par défaut

Enfin, dernière petite chose à noter. La méthode subcommand() de la structure ArgMatches renvoie un tuple (c’est à dire deux variables) : une chaine de caractère, et une autre instance d’ArgMatches contenant des informations propre à la sous-commande donnée.

Je peux donc maintenant récupérer le nom du projet, via la methode value_of de cette nouvelle instance d’ArgMatches retournée, pour au final, le donner comme paramètre de ma fonction create_project (qui contiendra les instructions pour générer le squelette du projet en C).

("create", Some(create_matches)) => create_project("./", create_matches.value_of("PROJECT_NAME").unwrap()), // Dans le cas où une sous-commande s'appellerait "create", on appelle la function create_project (qui est définie plus bas dans le fichier main.rs), en lui passant en paramètre le chemin courrant, et le nom du projet récupéré de l'argument PROJECT_NAME.

 

Enfin, dans le cas où le tuple retourné contient une chaine de caractère vide (c’est à dire qu’aucune sous commande n’a été renseignée) , j’affiche un message.

Cela permet de ne plus avoir de résultat vide quand on lance le programme, comme un peu plus haut.

("", None) => println!("No subcommand was used"),

 

Dorénavant, si je lance le programme sans sous-commande, j’obtiens :

$ cargo run --

Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\article.exe`
No subcommand was used

 

Nickel non ?

Et ça clôture cet article !

Finalement, il est un poil plus longuet que ce que j’avais imaginé, mais dans tous les cas, c’est toujours cool de terminer sur un « Nickel » ! Pas vrai ?

Plus sérieusement, merci d’avoir pris la peine de lire jusqu’au bout !

J’espère que ça vous aura plu, et que ce n’était pas trop pénible à lire (n’hésitez pas d’ailleurs à me faire savoir via les commentaires ci-dessous vos avis, et si vous avez des idées pour améliorer la présentation de l’article).

Dans tous les cas, à très vite pour la suite ! Car pour l’instant, le suspens reste encore entier sur ce qui se cache derrière la fonction create_project !

 

 

 

Merci d’avoir pris le temps de lire cet article. Est-ce que cela vous a intéressé ? Est-ce que vous avez appris quelque chose ? N’hésitez pas à me partager votre opinion sur le sujet via les commentaires ci-dessous.

Et si vous aimez ce contenu, vous pouvez vous abonnez à ma newsletter (si le coeur vous en dit), afin de rester informé des dernières nouveautés du blog, et également pour recevoir (pas plus d’une fois par semaine), d’autres contenus que je juge intéressant sur tout ce qui touche la programmation ou le développement personnel.

Inscription à la newsletter :

[newsletter]

 

Victor Gallet

Victor Gallet

Étudiant programmeur jeu vidéo. J'aime par dessus tout apprendre, et je suis un éternel curieux de tout. Mon principal but dans la vie est d’être une meilleure personne, et de partager mes (faibles) connaissances avec les autres.

Leave a Reply

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.