Introduction
La revue de code est une partie intégrante du développement professionnel. Dans l'écosystème Rust, où la sécurité mémoire et les performances sont primordiales, la revue de code revêt une importance particulière. Une utilisation incorrecte de unsafe, l'ignorance des règles de propriété ou les paniques implicites peuvent annuler tous les avantages du langage.
Dans cet article, nous examinerons ce qu'il faut rechercher lors d'une revue de code en Rust, comment structurer les retours et quels outils peuvent aider à automatiser les vérifications. Ce matériel sera utile aussi bien aux débutants qu'aux développeurs expérimentés cherchant à optimiser le processus de revue.
1. Sécurité : vérification du code unsafe et des paniques implicites
Rust garantit la sécurité mémoire, mais unsafe est une renonciation à ces garanties. Chaque bloc unsafe doit être soigneusement vérifié.
Que vérifier dans le code unsafe ?
- Exactitude des pointeurs : pas de déréférencement de pointeurs nuls ou pendants.
- Alignement des données : alignement correct lors du transtypage de pointeurs bruts.
- Aliasing : pas de violations des règles d'aliasing (ex : emprunt mutable et immuable simultané).
- Invariants : maintien des invariants des structures de données (ex : un tas binaire reste un tas).
// Bad example: unsafe cast without alignment checkfn bad_unsafe(data: &[u8]) -> &[u32] { unsafe { std::slice::from_raw_parts( data.as_ptr() as *const u32, data.len() / 4 ) }}
// Good example: with alignment checkfn good_unsafe(data: &[u8]) -> Option<&[u32]> { if data.len() % 4 != 0 || data.as_ptr() as usize % 4 != 0 { return None; } Some(unsafe { std::slice::from_raw_parts( data.as_ptr() as *const u32, data.len() / 4 ) })}Paniques implicites
En Rust, de nombreuses opérations peuvent provoquer une panique : unwrap(), expect(), panic!(), division par zéro, accès à un tableau hors limites. Lors de la revue de code, faites attention à :
- Utiliser
unwrap()uniquement dans les tests ou lorsqu'une erreur est vraiment impossible. - Préférer
unwrap_or(),unwrap_or_else()ou la gestion viamatch. - Vérifier que toutes les conversions
asne perdent pas de données (ex :u64 as u32).
// Bad: implicit paniclet value = some_option.unwrap();
// Good: explicit handlinglet value = some_option.unwrap_or_else(|| { log::error!("Missing expected value"); default_value()});2. Propriété et emprunt : erreurs typiques
Le système de propriété est la principale caractéristique de Rust. Les erreurs du compilateur sont ici vos meilleurs amis, mais la revue de code permet de trouver des erreurs logiques que le compilateur ne détecte pas.
Clonage excessif
Un problème courant est le clonage là où une référence pourrait être passée. Cela réduit les performances et augmente l'utilisation mémoire.
// Bad: cloning the entire stringfn process(data: Vec<String>) { for item in data { let cloned = item.clone(); do_something(&cloned); }}
// Good: passing a referencefn process(data: &[String]) { for item in data { do_something(item); }}Emprunts et durées de vie inutiles
Vérifiez si la durée de vie des références est trop longue. Parfois, vous pouvez simplifier une signature de fonction en supprimant les annotations de durée de vie inutiles.
// Complex: excessive lifetimesfn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y }}
// If