Code propre en Rust : principes, idiomes et exemples pratiques

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi du code propre en Rust n'est pas qu'une question de style

Rust est un langage qui allie les performances du C++ à la sécurité mémoire, mais son système unique de possession (ownership) et d'emprunt (borrowing) nécessite une approche particulière de l'écriture de code. Les principes du Clean Code, formulés par Robert Martin, prennent une nouvelle dimension en Rust : ici, la propreté du code est directement liée à la correction, aux performances et à la facilité de maintenance.

Dans cet article, nous verrons comment appliquer les idiomes de Rust pour créer un code auto-documenté, sûr et efficace. Vous découvrirez comment remplacer les erreurs typiques des débutants par des solutions élégantes adoptées par la communauté Rust.

1. Gestion idiomatique des erreurs : de unwrap() aux motifs expressifs

L'un des premiers problèmes rencontrés par les débutants est l'abus de unwrap(). Dans le Clean Code, une erreur n'est pas une exception, mais une partie de l'architecture. Rust fournit les puissants types Option et Result pour un traitement explicite des cas limites.

Mauvais exemple (code sale)

fn parse_number(input: &str) -> i32 {
    input.parse::<i32>().unwrap()
}

fn main() {
    let val = parse_number("pas un nombre");
    println!("{}", val); // Panique !
}

Bon exemple (code propre)

use std::num::ParseIntError;

fn parse_number(input: &str) -> Result<i32, ParseIntError> {
    input.parse::<i32>()
}

fn main() {
    match parse_number("42") {
        Ok(n) => println!("Nombre : {}", n),
        Err(e) => eprintln!("Erreur : {}", e),
    }
}

Pourquoi est-ce du Clean Code ? La fonction indique explicitement qu'elle peut échouer. Le code appelant décide lui-même comment gérer l'échec — journaliser, réessayer ou transmettre l'erreur plus haut.

Technique avancée : l'opérateur ?

fn read_config(path: &str) -> Result<String, std::io::Error> {
    let content = std::fs::read_to_string(path)?;
    Ok(content)
}

L'opérateur ? est une élégante alternative aux expressions match verbeuses. Il remonte l'erreur si le résultat est Err, et extrait la valeur s'il est Ok. Cela réduit l'imbrication et améliore la lisibilité.

2. Possession et emprunt : concevez l'architecture, ne luttez pas contre le compilateur

Le système de possession en Rust peut sembler une contrainte, mais il aide en réalité à écrire du code propre. Les problèmes commencent lorsque le développeur tente de « tromper » le compilateur avec clone() ou Rc<RefCell> sans nécessité.

Anti-patron : clonage excessif

fn process_data(data: Vec<String>) {
    for item in data.clone() { // Pourquoi cloner ?
        println!("{}", item);
    }
    // data n'est plus utilisée
}

Solution propre : emprunt par référence

fn process_data(data: &[String]) {
    for item in data {
        println!("{}", item);
    }
}

fn main() {
    let my_data = vec!["a".to_string(), "b".to_string()];
    process_data(&my_data); // La possession reste dans main
    println!("Le vecteur est toujours accessible : {:?}", my_data);
}

Principe du Clean Code : portée minimale des mutations. Si une fonction ne doit pas modifier les données — acceptez une référence immuable &T. Si une modification est nécessaire — &mut T. Cela rend le code prévisible et sûr.

Idiome : transfert de possession pour la transformation

fn to_uppercase(mut text: String) -> String {
    text.make_ascii_uppercase();
    text // La possession est retournée
}

Ici, la fonction « absorbe » la chaîne, la modifie et la retourne. C'est efficace (sans copie) et explicite : l'appelant comprend que la variable d'origine n'est plus valide.

3. Fermetures et fonctionnel

Blogs

Book Recommendations