10 points par GN⁺ 2025-08-28 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • Rust améliore la productivité et la maintenabilité grâce à de solides garanties de sécurité, qui permettent de refactoriser en toute confiance même dans de grandes bases de code
  • Le compilateur détecte à l’avance les bugs liés à l’ordonnancement asynchrone et renforce la stabilité en empêchant les comportements indéfinis
  • Des langages comme TypeScript découvrent souvent les bugs asynchrones en production à cause d’un système de types plus permissif
  • Le système de types de Rust indique clairement l’impact des changements de code, ce qui renforce la confiance et l’envie d’expérimenter dans les projets complexes
  • Contrairement à Rust, Zig peut laisser passer des bugs dus à des fautes de frappe à cause de vérifications plus souples dans la gestion des erreurs, ce qui réduit sa fiabilité

Résumé et contexte

  • Le backend de Lubeno est écrit à 100 % en Rust, et sa base de code a atteint une taille où il devient difficile d’en garder une vue d’ensemble mentale
    • Dans les grands projets, il est généralement difficile de vérifier les effets de bord des modifications, ce qui entraîne une baisse de productivité
  • Les garanties de sécurité de Rust indiquent clairement l’impact des modifications de code, ce qui réduit la peur du refactoring
    • Cela contribue à améliorer la maintenabilité et la productivité sur le long terme
  • Cet article part d’un cas où le compilateur Rust a détecté un bug asynchrone pour explorer les avantages de Rust en matière de productivité

Exemple des garanties de sécurité de Rust

  • Situation : une structure est encapsulée dans un mutex pour permettre un accès concurrent, puis une opération asynchrone est exécutée après acquisition du verrou
    let lock = mutex.lock();  
    db.insert_commit(commit).await;  
    
  • Découverte du problème : rust-analyzer n’affichait pas d’erreur, mais une erreur de compilation est apparue dans le fichier de définition des routes
    .route("/api/git/post-receive", post(git::post_receive))  
                                         ^^^^^^^^^^^^^^^^^  
    error: future cannot be sent between threads safely  
    
  • Analyse de la cause :
    • Le framework web crée une tâche asynchrone pour chaque connexion HTTP, et l’ordonnanceur de tâches déplace ces tâches entre les threads
    • Le mutex doit être libéré sur le même thread, et un changement de thread au point .await peut provoquer un comportement indéfini
    • Le compilateur Rust suit la durée de vie du verrou et détecte la possibilité qu’il soit libéré depuis un autre thread
  • Solution : libérer le verrou avant .await
  • Portée : Rust empêche à la compilation des bugs asynchrones difficiles à reproduire en environnement de développement

Cas comparatif avec TypeScript

  • Situation : apparition d’un bug de redirection asynchrone dans du code TypeScript
    if (redirect) {  
        window.location.href = redirect;  
    }  
    let content = await response.json();  
    if (content.onboardingDone) {  
        window.location.href = "/dashboard";  
    } else {  
        window.location.href = "/onboarding";  
    }  
    
  • Cause du problème :
    • window.location.href ne redirige pas immédiatement mais planifie la redirection, et l’exécution du code se poursuit
    • Une condition de concurrence provoque ainsi une redirection non souhaitée
  • Solution : ajouter return dans le bloc if
    if (redirect) {  
        window.location.href = redirect;  
        return;  
    }  
    
  • Limite : TypeScript ne dispose ni de suivi de durée de vie ni de règles d’emprunt, et ne peut donc pas détecter ce type de bug à la compilation
    • Le problème est découvert en production, et le débogage prend beaucoup de temps

Les avantages de Rust pour le refactoring

  • En développement web, Python, Ruby et JavaScript/Node.js offrent une forte productivité initiale, mais quand la base de code grandit, leur couplage lâche rend les changements plus difficiles
    • Des erreurs inattendues apparaissent après modification, ce qui réduit la volonté de retoucher le code
  • Avec Rust, le système de types indique clairement l’impact des changements, ce qui réduit la peur du refactoring
    • Exemple : un avertissement du type « ce changement peut affecter d’autres parties » permet de prévenir les problèmes en amont
  • Même lorsque la base de code grossit, la productivité augmente, car on peut réutiliser le code existant et conserver sa stabilité lors des modifications

Comparaison avec les tests

  • Les tests sont utiles pour éviter les régressions lors du refactoring, mais comme ils ne sont pas imposés par le compilateur, ils peuvent être omis
    • Écrire des tests impose une charge mentale importante : il faut décider du niveau d’abstraction, du comportement par rapport aux détails d’implémentation, et de ce qu’il faut couvrir pour prévenir les erreurs
  • Rust permet au compilateur de bloquer à l’avance les erreurs courantes, ce qui réduit le poids des décisions liées aux tests
    • Les propriétés que le système de types ne peut pas vérifier sont alors complétées par des tests

Comparaison avec Zig

  • Zig est un langage de programmation système proche de Rust, mais plus permissif dans sa gestion des erreurs
    • Exemple de code de gestion d’erreur :
      const FileError = error{ AccessDenied };  
      fn doSomethingThatFails() FileError!void {  
          return FileError.AccessDenied;  
      }  
      pub fn main() !void {  
          doSomethingThatFails() catch |err| {  
              if (err == error.AccessDenid) {  
                  std.debug.print("Access was denied!\n", .{});  
              }  
          };  
      }  
      
    • À cause de la faute de frappe AccessDenid, un bug apparaît, mais le compilateur Zig traite cela comme un nombre et la compilation réussit quand même
  • Avec une instruction switch, la faute de frappe est détectée, mais elle est ignorée dans une instruction if, ce qui pose un problème de fiabilité
  • Rust évite ce type de faille de conception et vérifie strictement les fautes de frappe comme les erreurs logiques

Enseignements

  • Rust améliore la productivité et la stabilité des grands projets grâce à ses garanties de sécurité et à son système de types strict
  • Même des problèmes complexes comme les bugs asynchrones peuvent être détectés à la compilation, ce qui réduit les coûts de maintenance
  • Les exemples de TypeScript et Zig montrent les risques de vérifications trop souples et soulignent la valeur du compilateur strict de Rust
  • Rust s’impose aussi dans le développement web comme un outil puissant non seulement pour la productivité initiale, mais aussi pour la gestion à long terme de la base de code

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.