Clean Code en Rust : principes et exemples pour débutants
\nRust est un puissant langage de programmation système, réputé pour ses performances et sa sécurité mémoire. Cependant, comme dans tout autre langage, écrire du code propre (Clean Code) est une compétence cruciale. Un code propre est plus facile à lire, à maintenir et à étendre. Pour les développeurs débutants en Rust, maîtriser les principes du Clean Code dès le départ permet d'éviter les erreurs courantes et de rendre votre code élégant et fiable. Dans cet article, nous allons explorer les principes fondamentaux du code propre en Rust avec des exemples concrets.
\n\n1. Nommage des variables et des fonctions
\nUn bon nom doit expliquer l'intention du programmeur. En Rust, on utilise snake_case pour les variables, les fonctions et les modules, et CamelCase pour les types et les traits.
\n\nMauvais exemple :
\nfn c(a: i32, b: i32) -> i32 {\n let x = a + b;\n x\n}\n\nBon exemple :
\nfn calculate_sum(first_number: i32, second_number: i32) -> i32 {\n let result = first_number + second_number;\n result\n}\nUtilisez des noms significatifs : user_name au lieu de un, process_data au lieu de pd. Évitez les abréviations, sauf si elles sont universellement reconnues (par exemple, idx pour index).
2. Petites fonctions et responsabilité unique
\nLe principe de responsabilité unique (Single Responsibility Principle) stipule que chaque fonction doit faire une seule chose. Si une fonction effectue plusieurs actions, elle devient difficile à tester et à modifier.
\n\nMauvais exemple (fonction qui fait trop de choses) :
\nfn process_user(user_id: u32) {\n // Récupération de l'utilisateur depuis la base de données\n // Validation des données\n // Envoi d'email\n // Journalisation\n println!("Traitement de l'utilisateur {}", user_id);\n}\n\nBon exemple (division en petites fonctions) :
\nfn get_user(user_id: u32) -> User {\n // Logique de récupération de l'utilisateur\n User { id: user_id }\n}\n\nfn validate_user(user: &User) -> bool {\n // Logique de validation\n user.id > 0\n}\n\nfn send_email(user: &User) {\n // Logique d'envoi d'email\n println!("Email envoyé à l'utilisateur {}", user.id);\n}\n\nfn process_user(user_id: u32) {\n let user = get_user(user_id);\n if validate_user(&user) {\n send_email(&user);\n }\n}\nEssayez de limiter chaque fonction à 10-20 lignes. Si elle est plus longue, divisez-la.
\n\n3. Gestion des erreurs : utilisez Result et Option
\nRust encourage la gestion explicite des erreurs via les types Result<T, E> et Option<T>. Évitez d'utiliser .unwrap() ou .expect() dans le code de production — cela provoque une panique en cas d'erreur.
Mauvais exemple (extraction dangereuse) :
\nfn read_file(path: &str) -> String {\n let content = std::fs::read_to_string(path).unwrap(); // Peut planter !\n content\n}\n\nBon exemple (gestion d'erreur) :
\nuse std::fs;\n\nfn read_file(path: &str) -> Result<String, std::io::Error> {\n let content = fs::read_to_string(path)?; // Opérateur ? pour propager l'erreur\n Ok(content)\n}\n\nfn main() {\n match read_file("data.txt") {\n Ok(data) => println!("Contenu du fichier : {}", data),\n Err(e) => eprintln!("Erreur de lecture du fichier : {}", e),\n }\n}\nUtilisez l'opérateur ? pour la concision, mais gérez toujours les erreurs au niveau supérieur. Pour vos propres erreurs, créez des énumérations avec le trait std::error::Error.
4. Commentaires et documentation
\nUn code propre doit être auto-documenté. Les commentaires expliquent le pourquoi, pas le quoi. Pour documenter les API publiques, utilisez /// (doc comments).
Mauvais exemple (commentaire inutile) :
\n// Incrémente le compteur de 1\ncounter += 1;