Rust QC
Cette compétence établit un standard unique pour le code Rust et l'applique selon trois modes. Les règles proviennent du Rust Style Guide, des Rust API Guidelines, des Pragmatic Rust Guidelines de Microsoft (2026.6), du Rust Style Guide de Google, ainsi que des pratiques observées dans memchr, jiff, ripgrep, fd et dua-cli. Lorsque ces sources divergent, references/precedence.md enregistre la décision afin que vous n'ayez jamais à la rediscuter.
Étape 1 : Établir le contexte (tous les modes)
Effectuez cette étape avant d'écrire ou de juger du code. Arrêtez-vous et signalez si une condition préalable échoue ; ne devinez pas en l'absence d'une toolchain ou d'un manifeste.
- Trouvez la racine de la crate (le
Cargo.tomlle plus proche). Notez s'il s'agit d'un workspace, d'une bibliothèque, d'un binaire, ou des deux, et si elle est publiée (publish = falseou consommée uniquement à l'intérieur du workspace ou via FFI signifie interne). - Lisez d'abord les propres règles du projet, car elles remplacent les valeurs par défaut de cette compétence :
rustfmt.tomlou.rustfmt.toml,clippy.toml,[lints]et[workspace.lints]dansCargo.toml,edition,rust-version(MSRV),rust-toolchain.toml,CONTRIBUTING.md,AGENTS.mdouCLAUDE.md, et toutAI_POLICY.md. SiAI_POLICY.mdouCONTRIBUTING.mdrestreint les contributions d'IA ou d'agents, informez l'utilisateur avant de produire des modifications destinées à ce dépôt. - Classez le code comme bibliothèque ou application. Les règles de conception d'API sont contraignantes pour les bibliothèques et de fortes recommandations pour les applications, qui doivent privilégier la simplicité à la généralité.
- Notez les plateformes. Si
rust-toolchain.tomlliste des cibles ou si le code est riche en#[cfg(windows)]pour une plateforme autre que l'hôte actuel, exécutez également la vérification avec--target <triple>(cela vérifie les lints et documente pour cette cible ; les tests sont ignorés). Les avertissements tels que le code mort qui n'apparaissent que sur l'hôte en raison de barrières de plateforme sont des artefacts de l'hôte, et non des problèmes. - Exécutez
scripts/check.sh <crate-root>une fois pour enregistrer la référence. Vous en avez besoin pour distinguer vos propres régressions de la dette préexistante. Utilisez--features "<liste>"au lieu du--all-featurespar défaut lorsque la crate a des fonctionnalités mutuellement exclusives.
N'installez pas de toolchains, composants ou outils (nightly, Miri, cargo-hack) et n'utilisez pas le réseau au-delà du téléchargement de crates, sauf si l'utilisateur le demande. Lorsqu'une vérification nécessite quelque chose manquant, dites-le et recommandez-le.
Étape 2 : Choisir le mode
Créer lorsque l'utilisateur souhaite de nouvelles fonctions, méthodes, types, modules ou crates. Suivez "Créer du code" ci-dessous.
Modifier lorsque l'utilisateur souhaite modifier du code existant : une fonctionnalité, un raffinement, une correction de bug, un refactoring ou une remédiation de résultats d'audit. Suivez "Modifier du code" ci-dessous.
Auditer lorsque l'utilisateur souhaite que du code existant soit examiné, évalué ou vérifié, avec des problèmes identifiés et des correctifs proposés. Suivez "Auditer du code" ci-dessous. L'audit ne modifie pas le code sauf si l'utilisateur le demande ; il produit des constats.
Une demande peut couvrir plusieurs modes ("auditez ce module et corrigez ce que vous trouvez"). Effectuez d'abord l'audit, rapportez, puis passez en mode modification pour les correctifs.
Règles non négociables
Ces règles s'appliquent dans tous les modes. L'ensemble complet des règles, avec des identifiants, se trouve dans les fichiers de référence listés à la fin.
- Le formatage est le travail de rustfmt. Ne formatez jamais manuellement contre rustfmt. N'ajoutez jamais
#[rustfmt::skip]sauf pour un alignement délibéré (tableaux, matrices) ou du code généré. - Le code est sûr. Le code safe ne doit pas pouvoir causer de comportement indéfini. Chaque bloc
unsafea un commentaire// SAFETY:directement au-dessus expliquant pourquoi chaque condition préalable est remplie. Chaqueunsafe fnetunsafe traita une section de documentation# Safety. Utilisezunsafeuniquement pour FFI, un besoin de performance mesuré, ou une abstraction novatrice, et jamais pour contournerSend,Syncou les durées de vie. Voirreferences/unsafe.md. - Erreurs vs paniques. Les échecs récupérables causés par l'entrée ou l'environnement retournent
Result(ouOptionlorsque la cause est évidente). Les bugs de programmation détectés provoquent une panique. Le code de bibliothèque ne panique jamais sur une entrée de l'appelant qui pourrait raisonnablement être invalide. Voirreferences/errors.md. - Les erreurs de bibliothèque permettent aux appelants de se brancher sans analyser le texte. Un type d'erreur concret avec une méthode
kind()retournant une énumération#[non_exhaustive]publique, ou des prédicatsis_*(). Les champs privés sont appropriés ; un type privé sans accesseur est un défaut. Les messages pointent vers le jeton et la position fautifs, et ne copient jamais l'intégralité de l'entrée. - Pas de
unwrap()nu dans le code de production. Utilisez?, le filtrage par motif, ouexpect("...")dont le message indique pourquoi l'invariant est maintenu.unwrap()est acceptable dans les tests. Les exemples de documentation utilisent?avec la ligne cachée# Ok::<(), Box<dyn std::error::Error>>(()). - Chaque nouvelle dépendance est un coût, y compris les dev-dependencies. N'en ajoutez une que si elle supprime une complexité réelle ou apporte une correction que vous ne pouvez pas écrire raisonnablement (la pliage de cas Unicode est une bonne raison ; un framework de tests de propriétés pour un analyseur de 100 lignes ne l'est pas). Dites pourquoi dans votre rapport.
- La structure correspond à la taille. Ne créez pas de module pour une petite fonction et son type d'erreur ; un module mérite son fichier avec un concept distinct et plusieurs éléments ou quelques centaines de lignes.
- Les remplacements de lints utilisent
#[expect(lint, reason = "...")], pas#[allow], afin que les remplacements obsolètes soient détectés. - Les éléments publics sont documentés, la première phrase tient sur une ligne d'environ 15 mots, et les fonctions incluent les sections
# Errors,# Panicset# Safetylorsqu'elles s'appliquent. Voirreferences/documentation.md. - Les types publics implémentent
Debug. Les types destinés à être lus par des humains implémentent égalementDisplay. - Les commentaires expliquent pourquoi, pas quoi. Si un commentaire répète le code, supprimez-le. Le commentaire sur l'allocateur de ripgrep dans
crates/core/main.rsest le modèle : décision, raison et conditions dans lesquelles il s'applique. - Pas de diagrammes ASCII-art ou de caractères de boîte dans les commentaires ou la documentation. Décrivez la structure en phrases ; les dessins sont illisibles avec un lecteur d'écran et se détériorent lorsque le code change. (Convention d'équipe, pas en amont.)
- Pas de contenu de journal de conception dans la documentation. La documentation décrit le comportement actuel, pas comment le code est né ou quelles directives ont été suivies.
- Les tests vérifient le comportement. Pas de tests qui répètent une constante ou reflètent les branches de l'implémentation.
Créer du code
Travaillez de l'extérieur vers l'intérieur : d'abord les types et les signatures, puis la documentation, puis l'implémentation, puis les tests. Écrire le commentaire de documentation avant le corps est le moyen le plus rapide de découvrir une API confuse.
- Placez-le. Mettez l'élément dans le module qui possède son concept ; dans une petite crate, c'est souvent
lib.rs. Les nouveaux modules sont nommés d'après leur type principal et créés uniquement lorsque le code le justifie (ORG-19). Voirreferences/organization.md. - Modélisez avec des types. Des nouveaux types pour les valeurs faciles à confondre ou qui portent des invariants, avec des constructeurs qui imposent l'invariant. Une
enumplutôt qu'un paramètrebooldont la signification n'est pas évidente au site d'appel. Des champs nommés plutôt que des tuples. Rendez les états invalides irreprésentables. - Façonnez la signature. Empruntez ce que vous lisez seulement (
&str,&[T],&Path,Option<&T>), prenez possession uniquement lorsque vous stockez ou consommez. Dans les bibliothèques, acceptezimpl AsRef<Path>ouimpl IntoIteratorlorsque cela réduit les frictions ; dans les applications, gardez des types concrets. Des builders pour les types avec plusieurs paramètres optionnels, validant dansbuild(). Voirreferences/api-design.md. - Nommez-le. Casse RFC 430 ; conversions
as_/to_/into_; constructeursnew,with_*,try_new; accesseursfoo(). Pas de mots vagues (Manager,Service,Factory,Helper,Util), et aucun nom qui entre en conflit avec une signification existante dans la même interface (NAME-9). - Documentez-le selon
references/documentation.md, y compris un exemple exécutable pour les éléments publics, et l'unité de toute position ou longueur que vous retournez. - Implémentez-le. Des fonctions à usage unique. Une boucle
forsimple plutôt qu'une chaîne de combinateurs dès que la chaîne a besoin de conditionnelles ou d'état externe ; des combinateurs lorsqu'ils remplacent une logique écrite à la main (any,position,sum). Des annotations de type lorsque l'inférence laisserait un lecteur dans le doute. Le texte humain est traité avec une gestion consciente d'Unicode : la correspondance insensible à la casse utilise le pliage de cas Unicode, ou documente ses lacunes (API-27). - Gérez les erreurs selon
references/errors.md: dans les bibliothèques, un type d'erreur concret avec un genre public ou des prédicats ; dans les applications,anyhowavec.context(). - Testez-le selon
references/testing.md: tests unitaires en ligne, tests d'intégration soustests/, et vérifications larges d'invariants qui ne nécessitent pas de nouvelles dépendances (énumérez les entrées courtes, ou une boucle ensemencée contre une référence naïve). - Vérifiez avec la validation ci-dessous.
Modifier du code
L'objectif est le plus petit changement correct qui laisse le code touché au moins aussi clair que vous l'avez trouvé.
- Lisez avant de modifier. Lisez toute la fonction, ses appelants, ses tests et l'API voisine. Identifiez les conventions locales (style d'import, type d'erreur, style de titre de documentation tel que
# Examplevs# Examples, largeur de ligne, densité, noms de paramètres et de clés) et respectez-les, même si les valeurs par défaut de cette compétence diffèrent. - Les corrections de bugs commencent par un test qui échoue et couvre la classe de bug. Reproduisez l'entrée signalée, puis ajoutez les cas voisins que la même cause racine brise (plus d'éléments, d'autres ordres, combinaisons). Regardez-les échouer, corrigez la cause racine, regardez-les passer. Si le projet conserve les tests de régression à un seul endroit (
tests/regression.rschez ripgrep), ajoutez le vôtre là-bas. - Corrigez la cause, pas le symptôme. Un nouveau
unwrap_or_default(),clone(), ou retour précoce qui réduit un échec au silence n'est pas un correctif à moins que vous puissiez expliquer pourquoi le chemin original était erroné. Mettez un commentaire d'une ligne au correctif si la raison n'est pas évidente d'après le code. - Restez chirurgical. Ne renommez pas, ne réorganisez pas et ne reformatez pas le code qu'on ne vous a pas demandé de toucher. Listez les problèmes non liés pour l'utilisateur plutôt que de les corriger silencieusement.
- Maintenez la stabilité des API et ABI publiques sauf si l'utilisateur a approuvé un changement cassant. Ajouter un champ à une structure publique avec des champs publics, une variante à une énumération publique exhaustive, une méthode de trait obligatoire, ou modifier un contrat FFI est cassant.
- Mettez à jour la documentation et les tests avec le code. Un changement de signature ou de comportement implique des
# Errors,# Panics, des exemples et des tests modifiés dans le même changement. - Formatage : si la référence était propre à rustfmt, exécutez
cargo fmt. Si ce n'était pas le cas, formatez uniquement ce que vous avez changé et informez l'utilisateur. Sirustfmt.tomlutilise des options réservées à nightly (rustfmt stable avertit qu'elles sont "only available in nightly channel"), la norme du projet estcargo +nightly fmt; utilisez-la si installée, sinon ne formatez rien automatiquement et dites-le, car rustfmt stable réécrirait le code contrairement aux paramètres du projet. - Remédiation suite à un audit : corrigez les constats par ordre de gravité, un constat par changement logique, et nommez le constat dans votre résumé.
- Vérifiez avec la validation ci-dessous et confirmez que vous n'avez introduit aucun nouvel avertissement par rapport à la référence, sur chaque cible vérifiée à l'étape 1.
Auditer du code
-
Exécutez
scripts/check.sh --audit <crate-root>(plus--targetselon l'étape 1). Le passage--auditrapporte, avec l'accessibilité de l'API publique,unsafenon documenté, la documentation manquante et les sections# Errors/# Panics,unwrapen dehors des tests,#[allow]sans raison,dbg!/todo!, les importations glob, et les assertions sans message. Rapportez ces résultats d'hygiène de manière compacte : un constat Mineur par catégorie (documentation,Debug, commentaires SAFETY, configuration des lints), chacun avec un compte et les quelques exemples les plus importants. Un échec d'étape uniquement dû à une toolchain plus récente ici que celle utilisée par le CI du projet ourust-toolchain.tomlest une dérive de toolchain : un constat Mineur, pas un Majeur. -
Exécutez
python3 -I scripts/audit_scan.py <crate-root>pour ce que clippy ne peut pas voir : réexportations glob, noms de types vagues, commentaires ASCII-art, documentation d'historique de conception, tableaux de paramètres, TODO nus, messagesexpectvides et macros d'impression dans le code de bibliothèque. Confirmez chaque résultat en lisant le code ; rejetez les faux positifs. -
Sondez le comportement comme les utilisateurs le rencontrent. C'est là que la plupart des bugs visibles par l'utilisateur dans les vraies crates sont trouvés, donc passez la majeure partie de l'audit ici et à l'étape 5. Lisez la documentation, les spécifications et les tests de la crate pour lister les fonctionnalités d'entrée qu'elle prétend gérer. Pour chacune, construisez une petite entrée réaliste que les tests existants ne couvrent pas, exécutez-la via l'API publique à partir d'une crate jetable en dehors du workspace, et comparez la sortie avec ce qu'un utilisateur devrait obtenir. Couvrez les fonctionnalités que le format permet mais que les fixtures ignorent :
- valeurs héritées, indirectes ou par défaut
- doublons
- parties optionnelles ou cachées
- dommages légers que d'autres outils tolèrent
- texte non-ASCII et encodages legacy
- tailles grandes mais légitimes
Rapportez chaque problème confirmé avec l'entrée qui le montre.
-
Testez les entrées non fiables. Pour chaque fonction publique qui analyse des octets ou du texte non fiables, nourrissez-la avec chaque entrée courte sur un alphabet pertinent ou quelques millions de mutations aléatoires ensemencées, et interceptez les paniques. Essayez également des structures conçues pour faire croître le travail plus vite que la taille de l'entrée (références partagées ou récursives, imbrication profonde) sous un délai d'attente.
-
Lisez le code pour ce que les outils ne peuvent pas juger : la sûreté de chaque bloc
unsafe, la conception des erreurs, la forme de l'API, les limites des modules, les noms, la qualité des tests, et si les commentaires expliquent pourquoi. Dans le code qui interagit avec le système d'exploitation, vérifiez également :- les paniques sur un syscall, une E/S ou un échec d'environnement (
assert!ouunwrapsur une valeur de retour, une variable d'environnement ou un champ passwd non UTF-8) - le code entre
forketexec(closurespre_exec) ou dans un gestionnaire de signal qui alloue, verrouille, formate, journalise ou quitte, rien de quoi n'est async-signal-safe - les descripteurs de fichier ou poignées fuites, et les signaux envoyés à un ID de processus qui a pu être récupéré et réutilisé
- les conversions rétrécissantes et l'arithmétique non vérifiée sur les tailles, comptes et coordonnées
Utilisez
references/audit-checklist.md. - les paniques sur un syscall, une E/S ou un échec d'environnement (
-
Rapportez en utilisant le format ci-dessous. Proposez un correctif concret pour chaque constat.
Les étapes 1 et 2 sont un plancher rapide, pas l'audit : n'y passez pas plus d'environ un cinquième du temps. Un audit qui liste principalement des lints, de la documentation, et...