Introduction
Rust est un puissant langage de programmation système qui gagne en popularité grâce à sa sécurité et ses performances. Cependant, son système de possession (ownership) et d'emprunt (borrowing) déroute souvent les débutants. Dans cet article, nous analyserons les principales erreurs des développeurs Rust débutants, afin que vous puissiez les éviter et vous familiariser plus rapidement avec le langage. Nous examinerons les problèmes typiques liés à l'emprunt, aux durées de vie (lifetimes), aux chaînes de caractères et à la gestion de la mémoire.
1. Ignorer les règles d'emprunt (Borrow Checker)
L'erreur la plus courante est la tentative d'avoir simultanément une référence mutable et une référence immuable sur les mêmes données. Rust l'interdit strictement lors de la compilation.
Problème : références multiples
fn main() {
let mut data = vec![1, 2, 3];
let ref1 = &data; // référence immuable
let ref2 = &mut data; // référence mutable — ERREUR !
println!("{} {}", ref1[0], ref2[0]);
}
Le compilateur générera une erreur : cannot borrow `data` as mutable because it is also borrowed as immutable.
Solution : portée correcte
fn main() {
let mut data = vec![1, 2, 3];
{
let ref1 = &data; // référence immuable
println!("{}", ref1[0]);
} // ref1 sort de la portée
let ref2 = &mut data; // maintenant c'est possible
ref2[0] = 10;
println!("{}", ref2[0]);
}
Surveillez toujours la durée de vie des références. Si vous devez travailler avec une référence mutable et une référence immuable, séparez-les dans différents blocs.
2. Mauvaise gestion des chaînes : String vs &str
Les débutants confondent souvent String (type possédant, mutable) et &str (référence à une tranche de chaîne). Cela entraîne des erreurs lors du passage de chaînes aux fonctions.
Problème : attente de possession au lieu d'une référence
fn print_length(s: String) {
println!("Longueur : {}", s.len());
}
fn main() {
let name = "Alice".to_string();
print_length(name);
println!("Nom : {}", name); // ERREUR ! name a été déplacé
}
Solution : utilisez &str pour les paramètres
fn print_length(s: &str) {
println!("Longueur : {}", s.len());
}
fn main() {
let name = "Alice".to_string();
print_length(&name); // on passe une référence
println!("Nom : {}", name); // maintenant ça fonctionne
}
Règle : si une fonction ne doit pas prendre possession, utilisez &str. Cela rend le code flexible — vous pouvez passer à la fois des &String et des littéraux de chaîne "texte".
3. Erreurs avec les durées de vie (Lifetimes)
Lorsqu'une fonction retourne une référence, Rust exige de spécifier combien de temps cette référence va vivre. Les débutants oublient souvent les annotations de lifetimes.
Problème : lifetimes implicites
fn longest(x: &str, y: &str) -> &str { // ERREUR : spécificateur de durée de vie manquant
if x.len() > y.len() { x } else { y }
}
Solution : ajoutez une durée de vie commune
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("court");
let s2 = "longue chaîne";
let result = longest(&s1, &s2);
println!("La plus longue : {}", result);
}
L'annotation 'a indique au compilateur que la référence retournée ne vivra pas plus longtemps que le paramètre d'entrée le plus court. Cela garantit la sécurité de la mémoire.
4. Ignorer Option et Result
Rust n'a pas de null. À la place, on utilise Option<T> pour une absence possible de valeur et Result<T, E> pour la gestion des erreurs. Les débutants essaient souvent de déréférencer Option sans vérification.
Problème : panique avec unwrap
fn find_user(id: u32) -> Option<String> {
if id == 1 {
Some("Anna".to_string())
} else {
None
}
}
fn main() {
let user = find_user(2);
println!("{}", user.unwrap()); // PANIQUE ! user est None
}
Solution : utilisez match ou if let
fn main() {
let user = find_user(2);
match user {
Some(name) => println!("Utilisateur trouvé : {}", name),
None => println!("Utilisateur non trouvé"),
}
// Alternative avec if let
if let Some(name) = find_user(1) {
println!("Utilisateur trouvé : {}", name);
}
}
Traitez toujours les cas None et Err explicitement. Utilisez unwrap() uniquement lorsque vous êtes absolument sûr que la valeur existe, ou dans du code prototype.