Les erreurs du compilateur Rust les plus couramment rencontrées dans RustRover – 1ère Partie

Read this post in other languages:

Le compilateur Rust est une créature pointilleuse et exigeante. S’il est mécontent du code source, il peut renvoyer plus de 400 erreurs différentes (sans compter ceux qui sont ajoutés chaque mois !). Certaines de ces erreurs sont extrêmement rares, tandis que d’autres compliquent le travail des développeurs Rust au quotidien. Dans le premier article de cette série, nous examinons une partie des messages d’erreur du compilateur Rust que les développeurs rencontrent le plus fréquemment dans RustRover, l’IDE de JetBrains conçu pour la programmation en Rust, et vous donnons des conseils pour éviter ces erreurs. Commençons par préciser ce que nous entendons par « erreurs les plus les plus couramment rencontrées ».

Identification des erreurs les plus courantes à partir des données d’utilisation de RustOver

Tout utilisateur de RustRover peut donner son accord pour que JetBrains dispose de ses données d’utilisation anonymisées. Plus le nombre d’utilisateurs acceptant de partager leurs données d’utilisation est important et plus ils utilisent RustRover, plus nous sommes en mesure de comprendre leur expérience et d’améliorer les fonctionnalités d’assistance au codage de l’IDE. Nous profitons donc de cette occasion pour remercier toutes les personnes qui partagent leurs données d’utilisation ! La confidentialité des données étant une priorité, l’IDE ne collecte que des informations très limitées, qui ne permettent en aucun cas de remonter à l’utilisateur. Ces données anonymes nous permettent aussi d’identifier les types de messages d’erreur générés le plus fréquemment.

Lorsqu’un utilisateur ayant donné son consentement lance la commande Cargo Build depuis l’IDE (par exemple, en déclenchant une configuration Run qui nécessite la compilation d’un projet) et que le compilateur Rust signale une erreur, nous enregistrons un code d’erreur. Cela n’inclut pas tous les problèmes qui surviennent pendant que les utilisateurs écrivent leur code, mais seulement ceux qui persistent après qu’ils aient créé leur projet. Les erreurs intermédiaires peuvent souvent être corrigées à l’aide des inspections et des correctifs rapides fournis par l’IDE. 

Nous avons collecté les codes d’erreur des utilisateurs de RustRover et avons classé les erreurs en fonction de leur fréquence. Cet article porte sur les erreurs figurant de la 10ème à la 6ème place et le prochain révélera les cinq erreurs les plus fréquentes. Nous allons examiner les causes de ces erreurs en utilisant des exemples simples et voir quels sont les moyens de les corriger.

Erreur fréquente n° 10 : E0412 (Un nom de type utilisé n’est pas dans la portée)

Rust fait une distinction stricte entre les sites de déclaration de type et les utilisations de nom de type. Chaque nom de type (y compris les types génériques) doit être déclaré quelque part et être disponible dans la portée dans laquelle il est utilisé. Lorsque le compilateur rencontre une utilisation de nom de type et n’a pas d’information sur son site de déclaration, il émet l’erreur E0412. Environ 12 % des utilisateurs de RustRover ont déjà fait l’expérience de cette erreur.

Imaginons que vous ayez saisi i42 au lieu de i32. RustRover détecte ce problème et met en évidence le nom de type inconnu. Le compilateur donne plus de détails et propose un correctif qui peut être facilement appliqué en cliquant sur le bouton Apply fix dans la sortie du compilateur :

Une erreur E0412 peut se produire dans d’autres situations, notamment :

  • Si l’on oublie de déclarer un type.
  • Lorsqu’un type est importé dans la portée actuelle.
  • En cas d’introduction d’un nom de type générique qui rend le type inaccessible au compilateur.

Pour résoudre le problème, vous pouvez fournir une déclaration de type (déclarer un struct ou introduire un nom de type générique correctement) ou amener le type dans une portée (via la clause use). L’explication officielle de l’erreur E0412 donne plus d’exemples.

Erreur fréquente n° 9 : E0061 (Un nombre non valide d’arguments a été passé lors de l’appel d’une fonction)

Bien que RustRover connaisse cette erreur et fournisse plusieurs correctifs, 13 % des utilisateurs la laissent passer et omettent de la corriger avant la création de leurs projets.

L’intitulé de cette erreur est assez explicite : une fonction est déclarée dans la portée actuelle ou importée depuis un autre endroit, et le site d’appel donne trop ou trop peu d’arguments. Voyons-en un exemple et comparons les suggestions de RustRover à celles du compilateur Rust :

Si on a l’habitude de programmer avec un autre langage, on pourrait être tenté de fournir un deuxième argument, en oubliant qu’en Rust, cette méthode n’en prend qu’un seul. RustRover et le compilateur Rust nous invitent donc à supprimer le second argument. Il est appréciable de bénéficier de cette suggestion de la part de l’IDE avant d’avoir créé le projet. Prêtez attention aux lignes de soulignement rouges ondulées dans votre code : généralement, elles sont là pour une bonne raison !

La situation devient plus intéressante si la fonction que nous appelons est définie dans notre propre code. Supposons que nous continuons de travailler sur le même exemple de code :

Dans ce cas, RustRover suggère comme première solution d’ajouter un paramètre à la fonction, ce qui peut faire sens. Le compilateur Rust insiste quant à lui pour le supprimer. Cette divergence a une explication logique. Le travail du compilateur consiste à s’assurer que le programme est correct, et le moyen le plus facile d’y parvenir consiste à éliminer l’argument supplémentaire sur le site de l’appel. En revanche, la mission de l’IDE est de vous accompagner vers la réalisation de votre objectif. Si vous avez saisi cet argument pour votre fonction, RustRover considère qu’il est probable que cela soit volontaire et tente donc de vous aider à terminer ce que vous avez commencé.

Erreur fréquente n° 8 : E0282 (Le compilateur n’a pas pu déduire un type et a demandé une annotation de type)

Parfois le compilateur est perdu : il ne trouve pas quel est le type requis pour une variable et ne peut que suggérer d’ajouter une annotation manuellement. Si vous avez déjà rencontré cette erreur, bienvenue au club : 13,5 % des utilisateurs de RustRover y ont déjà été confrontés.

La source principale des erreurs telles que E0282 est la généricité. De nombreuses fonctions de bibliothèques utilisent des paramètres de type générique, mais le compilateur doit instancier ces paramètres en type concret, ce qui est source de confusion pour lui. Prenons l’exemple suivant :

Ici nous devons collecter des nombres à partir d’une chaîne dans un conteneur. Malheureusement, le compilateur ne peut pas déterminer le type des nombres ou le genre de conteneur dont il s’agit.

Le compilateur suggère la spécification du type de conteneur en premier. Cependant, si nous appliquons ce correctif, nous obtiendrons de nouveau le même genre d’erreur, mais concernant str::parse cette fois. collect et parse sont des méthodes génériques, mais le compilateur a besoin de connaître le type précis afin de compiler le code qui les utilise. Veuillez noter que RustRover ne met pas les erreurs en évidence, car nous travaillons encore au perfectionnement de sa fonctionnalité de vérification de type.

Il existe plusieurs façons de corriger le problème, car une annotation de type peut être ajoutée à différents endroits. Nous pouvons spécifier le type concret du vecteur numbers :

let numbers: Vec = "1  5     6   3"

Ou nous pouvons mentionner le même type lors de l’appel de collect :

   .collect::<Vec>();

Enfin, nous pouvons mentionner différents types à différents endroits :

let numbers = "1  5     6   3"
   .split_whitespace()
   .map(str::parse::)
   .map(Result::unwrap)
   .collect::<Vec>();

Cette erreur est facile à corriger : il suffit de spécifier le type ou les types que vous voulez.

Erreur fréquente n° 7 : E0432 (Une importation n’a pas été résolue)

RustRover fournit de nombreuses fonctionnalités de saisie semi-automatique. Commençons par exemple en introduisant une expression régulière dans notre code :

Si vous choisissez la première suggestion, deux autres choses vont se produire en plus de la saisie semi-automatique elle-même :

  • Une dépendance de la crate regex sera ajoutée à votre fichier Cargo.toml.
  • Une clause use regex::Regex; sera ajoutée en haut du fichier.

Lorsque vous ajoutez des clauses d’utilisation comme cela, les importations sont écrites automatiquement et correctement. Mais il faut parfois écrire les importations manuellement et c’est là que l’erreur E0432 risque de se produire. 15,5 % des utilisateurs de RustRover la rencontrent occasionnellement, le plus souvent en cas de faute dans le nom d’une crate ou d’un module, de tentative d’importation de quelque chose d’inexistant ou de présence d’une clause use erronée dans le code après l’avoir copiée/collée à partir un autre endroit. La première suggestion est de toujours vérifier les dépendances et les noms.

Parfois, RustRover peut nous aider à éviter cette erreur. S’il a connaissance de la crate que nous tentons d’importer, il suggère l’ajout d’une dépendance lorsque vous collez du code depuis des sources externes ou fournit une assistance via les correctifs rapides suivants :

L’ajout de la dépendance correspondante à Cargo.toml corrige l’erreur immédiatement. Une fois que la crate est disponible, il est plus sûr d’utiliser la saisie semi-automatique pour les autres composants du chemin dans les clauses use, afin d’éviter la survenance de nouveaux problèmes avec les noms. Rappelez-vous aussi que la disponibilité de certains noms peut dépendre des fonctionnalités de la crate qui sont activées.

Il peut également y avoir des problèmes avec les noms de chemin spéciaux tels que super ou crate, notamment parce qu’ils sont traités différemment dans les différentes versions de Rust. Vous trouverez plus d’informations à ce sujet dans l’explication officielle.

Erreur fréquente n° 6 : E0382 (Une variable a été utilisée après que son contenu ait été déplacé)

Point intéressant : les problèmes liés à la possession ! 17 % des utilisateurs de RustRover ont déjà rencontré cette erreur. L’explication officielle est très détaillée et donne de nombreux exemples. Malheureusement, RustRover n’est pas d’une grande aide dans ce cas. Si un linter externe est désactivé, le mécanisme interne de RustRover ne voit aucun problème dans le code suivant :

fn main() {
   let vec = vec![1, 2, 3, 4, 5];
   let mut sum = 0;
   for v in vec {
       sum += v;
   }
   println!("Sum of {vec:?} elements is {sum}");
}

Ce code d’aspect anodin serait parfaitement légitime dans certains autres langages de programmation. Nous avons un vecteur et voulons calculer la somme de ses éléments. Si vous travaillez habituellement avec C et que vous ne connaissez pas les astuces de programmation liées aux itérateurs, vous écririez plutôt une boucle for traditionnelle à la place. Une fois les éléments ajoutés, il n’y aurait alors plus qu’à imprimer le vecteur et le résultat des calculs. Exact ?

Eh bien pas ce n’est pas le cas avec Rust, à cause des règles de la possession propres au langage. 

Le problème est que la source de données dans la boucle for est étendue à l’appel into_iter(), qui prend alors possession de l’ensemble du vecteur. Par conséquent, lorsque nous tenterons ensuite d’accéder aux éléments du vecteur dans println!, le compilateur indiquera qu’il a été déplacé.

Le correctif proposé par le navigateur est simple : itérer sur &vec pour éviter de le déplacer dans la boucle, puis l’emprunter .

En général, il est utile de conserver constamment une trace des règles de possession des valeurs. Le déplacement et l’emprunt de valeurs sont des concepts fondamentaux de Rust. Il est important pour les néophytes d’assimiler ces concepts dès le début.

Résumé

Dans ce premier article, nous avons passé en revue certaines des erreurs du compilateur Rust les plus fréquentes en nous basant sur les données d’utilisation de RustRover. Dans le prochain article, nous verrons les 5 erreurs les plus fréquentes et tenterons de répondre à une question que se posent tous les développeurs Rust : « Quelle partie de Rust cause le plus de problèmes ? ».

Auteur de l’article original en anglais :

Delphine Massenhove

Vitaly Bragilevsky

image description