Pourquoi la code review sur Rust n'est pas une simple vérification de parenthèses
Rust est un langage qui promet une gestion sécurisée de la mémoire et des performances élevées. Mais même le compilateur le plus strict ne protège pas contre les erreurs logiques, le code non idiomatique ou l'utilisation injustifiée de unsafe. La code review sur Rust est un art. Vous ne vérifiez pas simplement si les accolades sont fermées. Vous examinez comment le développeur a contourné (ou n'a pas contourné) les limitations du borrow checker, à quel point il utilise efficacement les itérateurs et s'il essaie de « duper » le système avec des pointeurs bruts.
Dans cet article, je vais vous dire sur quels aspects il faut se concentrer lors de la révision de code Rust. Pas de théorie superflue — seulement des conseils pratiques, des exemples et une checklist que vous pouvez accrocher au mur (ou sauvegarder dans vos favoris). C'est parti !
1. Possession, emprunt et durées de vie : la principale source de douleur
La première chose à vérifier est si le système de possession fonctionne correctement. Même si le code se compile, cela ne signifie pas qu'il est correct. Problèmes typiques :
- Clonage excessif. Lorsqu'une référence aurait suffi, le développeur copie les données. Cela tue les performances.
- Emprunt mutable et immuable. Si dans la même portée il y a
&mut Tet&Tsimultanément — ce sera une erreur. Mais parfois on peut réorganiser le code pour l'éviter. - Durées de vie (lifetimes) incorrectes. Surtout dans les structures complexes avec plusieurs références.
Exemple : mauvais code
fn process(data: &Vec<i32>) -> i32 { let mut sum = 0; for &x in data.iter() { sum += x; } // Tout va bien, mais pourquoi passer &Vec alors qu'on peut utiliser &[i32] ? sum}Bon code
fn process(data: &[i32]) -> i32 { data.iter().sum()}Que vérifier lors de la review : est-ce que &Vec, &String ou &HashMap sont passés là où une slice suffirait ? Les clone() sont-ils utilisés sans nécessité ? Les annotations de durée de vie sont-elles correctement placées ?
2. Unsafe : la zone rouge
Les blocs unsafe sont comme une trappe ouverte dans le Rust sécurisé. Ils doivent être justifiés, minimaux et soigneusement documentés. Lors de la code review, faites attention à :
- Pourquoi utiliser unsafe ? Souvent, on peut se contenter d'abstractions sécurisées.
- Exactitude des pointeurs bruts. Est-ce qu'un pointeur nul est déréférencé ? Une référence est-elle encore valide après la libération de la mémoire ?
- Invariants. Le code avec
unsafedoit décrire explicitement les invariants qu'il maintient.
Exemple : unsafe dangereux
unsafe fn dangerous() { let ptr = 0x12345 as *mut i32; *ptr = 42; // Et s'il n'y a rien à cette adresse ?}Un tel code ne devrait pas passer la review. Même si « ça marche » — ce n'est pas un argument.
Bon unsafe
unsafe fn init_buffer(buf: &mut [u8]) { // Nous sommes sûrs que buf pointe vers une mémoire valide, // car la partie appelante le garantit. std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());}Que vérifier : y a-t-il un commentaire justifiant chaque unsafe ? Y a-t-il des tests qui vérifient les cas limites ? Les garanties de sécurité sont-elles respectées ?
3. Fermetures et style fonctionnel
Rust encourage l'approche fonctionnelle : itérateurs, fermetures, map, filter, fold. Mais cela peut mener à une complexité excessive. Lors de la review, vérifiez :
- Les fermetures sont-elles abusées ? Si une fermeture capture beaucoup de variables, il est peut-être préférable d'extraire la logique dans une fonction séparée.