Introduction : pourquoi Rust semble difficile mais en vaut la peine
Rust est un langage qui promet une gestion sécurisée de la mémoire sans ramasse-miettes et des performances élevées. Cependant, ce sont précisément ces avantages qui deviennent une pierre d'achoppement pour les débutants. Le compilateur Rust est strict dans les moindres détails : il ne pardonne pas les comportements indéfinis, les pointeurs pendants ou les courses de données. Dans cet article, nous allons décortiquer le top des erreurs des développeurs Rust débutants — des plus fréquentes aux plus perfides. Vous apprendrez comment les éviter et accélérer votre chemin vers la maîtrise.
1. La lutte avec l'emprunt (Borrow Checker)
La "douleur" la plus célèbre des débutants est le système de possession (ownership) et d'emprunt (borrowing). Le compilateur ne vous permet pas de créer simplement une référence à des données si elles peuvent être modifiées ou supprimées.
1.1. Tentative de modifier des données via une référence immuable
L'une des premières erreurs est d'essayer de modifier une variable sur laquelle il existe déjà une référence immuable (&).
let mut x = 5;
let y = &x; // référence immuable
let z = &mut x; // ERREUR : impossible de créer une référence mutable tant qu'il y a une référence immuable
println!("{}", y);
Comment corriger : Assurez-vous que la portée des références immuables est terminée avant de créer une référence mutable. Utilisez des accolades pour limiter la portée.
let mut x = 5;
{
let y = &x; // la portée de y est limitée
println!("{}", y);
}
let z = &mut x; // maintenant c'est possible
1.2. Retour d'une référence à une variable locale
Les débutants essaient souvent de retourner une référence à des données créées à l'intérieur d'une fonction. Cela conduit à une erreur "dangling reference".
fn get_ref() -> &String {
let s = String::from("hello");
&s // ERREUR : s sera supprimée après la sortie de la fonction
}
Comment corriger : Retournez la possession (la chaîne elle-même) ou utilisez une durée de vie statique ('static) si les données vivent éternellement.
fn get_string() -> String {
let s = String::from("hello");
s // on transfère la possession
}
2. Utilisation incorrecte des lifetimes (durées de vie)
Lorsque vous commencez à travailler avec des fonctions qui acceptent plusieurs références, le compilateur exige de spécifier comment elles se rapportent en termes de durée de vie. Les débutants ignorent soit les lifetimes, soit écrivent 'static partout où ce n'est pas nécessaire.
2.1. Omission des annotations de lifetimes dans les signatures
Si une fonction accepte deux références et en retourne une, Rust ne peut pas déterminer lui-même laquelle "vit" le plus longtemps.
fn longest(x: &str, y: &str) -> &str { // ERREUR : un lifetime est nécessaire
if x.len() > y.len() { x } else { y }
}
Comment corriger : Ajoutez un paramètre de durée de vie commun.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
2.2. Utilisation excessive de 'static
'static signifie que la référence vit pendant tout le programme. L'utiliser pour des littéraux de chaîne est correct, mais pour des données dynamiques, c'est une mauvaise pratique.
fn get_static() -> &'static str {
"hello" // correct
}
fn get_dynamic() -> &'static str {
let s = String::from("hello");
&s // ERREUR : s ne vit pas éternellement
}
Conseil : Essayez de ne pas utiliser 'static sans nécessité. Il est préférable de transférer la possession ou d'utiliser Arc<Mutex<T>> pour la programmation concurrente.
3. Abus de unwrap() et panique
Rust encourage la gestion explicite des erreurs via Result et Option. Mais les débutants utilisent souvent .unwrap() ou .expect() partout pour "faire taire" le compilateur. Cela conduit à des paniques à l'exécution.