6 points par GN⁺ 2025-09-27 | 3 commentaires | Partager sur WhatsApp
  • En gestion mémoire, Zig propose une approche plus simple et plus intuitive que Rust
  • Le borrow checker de Rust est puissant, mais pour le développement de petits outils CLI, il entraîne une complexité excessive et une charge pour le développeur
  • La gestion mémoire manuelle de Zig permet d’obtenir efficacement une bonne sûreté mémoire avec les bons outils et un peu de discipline de la part du développeur
  • La sûreté d’un programme ne se limite pas à la sûreté mémoire : comportement prévisible, performances maîtrisées, protection des données et d’autres éléments sont tout aussi importants
  • Rust convient aux systèmes de grande envergure, mais pour de petits outils CLI pratiques, Zig est plus avantageux en productivité de développement et en maintenance

Vue d’ensemble

Ces derniers temps, lorsque je crée des outils CLI, j’ai tendance à choisir Zig en priorité plutôt que Rust.

Les bases de la gestion mémoire : pile et tas

  • La pile est une zone mémoire rapide à taille fixe qui stocke des données très temporaires comme les paramètres de fonction, les variables locales et les adresses de retour
  • Le tas est la zone dédiée à l’allocation dynamique de mémoire, utilisée lorsque la durée de vie des données est longue ou que leur taille est déterminée à l’exécution
  • La pile est structurellement simple mais l’espace y est limité, tandis que le tas demande plus d’attention en matière de vitesse et de fragmentation

Le Borrow Checker de Rust

  • Le borrow checker de Rust garantit la sûreté mémoire à la compilation
  • Il impose des règles sur les références, la propriété et les durées de vie (lifetime), ce qui permet d’éviter à l’avance des erreurs comme le déréférencement de pointeurs nuls ou les pointeurs pendants
  • En revanche, la sûreté mémoire n’est vérifiée qu’au moment de la compilation, et cela ne supprime ni les erreurs de l’utilisateur ni les problèmes inhérents à une conception complexe de la propriété

Cas pratique : mon propre CLI Notes

  • En essayant d’écrire en Rust un CLI pour gérer mes notes personnelles, j’ai dû repenser péniblement la structure à cause du borrow checker
  • À l’inverse, avec Zig, le simple usage d’allocateurs permet de créer des index basés sur des pointeurs et d’effectuer librement modifications et suppressions de façon bien plus simple
  • Le borrow checker de Rust a un objectif clair, mais Zig permet, avec des bases solides en gestion mémoire et un peu de discipline, d’atteindre un haut niveau d’efficacité et de sûreté

Quand la sûreté d’un outil CLI ne se résume pas à la sûreté mémoire

  • La véritable sûreté d’un produit inclut de nombreux éléments : comportement prévisible, retours utiles en cas d’erreur, protection des données sensibles, résistance aux attaques, etc.
  • Ni Rust ni Zig ne peuvent être qualifiés de « sûrs » s’ils ne remplissent pas aussi ces conditions au-delà de la seule sûreté mémoire
  • Par exemple, si un CLI écrase silencieusement des données en cas d’erreur ou configure mal les permissions de fichiers, l’utilisateur peut rencontrer de graves problèmes
  • La sûreté des outils CLI

    • Comportement prévisible : il faut garantir un comportement cohérent et clair, même avec une entrée invalide ou dans des situations imprévues
    • Prévention des crashs et de la corruption des données : il faut gérer les erreurs avec élégance et éviter la corruption des données ou les crashs non signalés
    • Maîtrise des performances : même en traitant de gros volumes de données, il ne doit pas y avoir de surconsommation de ressources ni de baisse de réactivité
    • Protection des informations sensibles : il faut prêter attention aux fichiers temporaires et aux réglages de permissions
    • Résistance aux attaques : il faut être robuste face à la validation des entrées, aux débordements mémoire, aux attaques par injection, etc.

Forces et limites du Borrow Checker de Rust

  • Forces

    • Blocage des data races et des références concurrentes invalides : le compilateur garantit les règles d’une unique référence mutable et de multiples références immutables
    • Fortes garanties à la compilation : la plupart des bugs liés à la mémoire sont bloqués avant l’exécution
    • Détection précoce des bugs : c’est un grand avantage dans les services en production ou les systèmes concurrents
  • Limites et inconforts

    • Surcharge cognitive : même pour de petites tâches CLI, il faut forcément réfléchir à la propriété, aux durées de vie et à la gestion des références
    • Boilerplate / distorsion structurelle : wrappers comme Rc et RefCell, abus de clone, refonte de structure, etc. font passer l’attention de la « résolution du problème » à la « satisfaction du compilateur »
    • Impuissance face aux bugs logiques ou d’état : seules les règles mémoire sont garanties ; la prévisibilité, les erreurs de logique ou l’intégrité des données ne le sont pas
    • Complexité des cas limites : des conflits de durées de vie surviennent facilement avec les caches, l’état global ou les index mutables
  • En conséquence, dans de petits projets CLI, le borrow checker de Rust devient une « taxe mentale » pour le développeur et peut rendre le tout plus complexe que nécessaire dans la pratique

L’approche de Zig en matière de sûreté et de simplicité

  • Zig repose sur des vérifications de sûreté optionnelles et une gestion mémoire manuelle
  • Le concept d’allocateur est intégré, ce qui permet de mettre en place une utilisation de la mémoire structurée et prévisible
  • Il est aussi possible de créer des allocateurs personnalisés pour définir une stratégie de gestion mémoire adaptée aux caractéristiques du projet
  • Grâce à l’instruction defer de Zig, la libération automatique en fin de portée et le nettoyage des ressources sont bien plus intuitifs
  • Contrairement à Rust, la responsabilité du développeur est mise en avant, ce qui demande de la discipline, mais avec une bonne conception, atteindre et maintenir la sûreté mémoire reste facile
  • Le code Zig est concis, et les modifications structurelles autour des pointeurs, listes et index sont bien plus simples qu’en Rust
  • Il est possible d’implémenter un code aussi sûr et efficace sans être autant contraint qu’en Rust
  • En plus, la fonctionnalité comptime de Zig aide énormément pour l’exécution de code à la compilation, les tests et les optimisations

L’importance de l’expérience développeur (Developer Ergonomics)

  • L’expérience développeur (ergonomics) englobe des éléments allant de la syntaxe du langage aux outils, à la documentation et à la communauté
  • Rust garantit au final la sûreté mémoire grâce à des règles très strictes, mais l’excès de règles et de cérémonial fait baisser la productivité
  • Zig met davantage l’accent sur une conception pilotée par le développeur, ce qui rend le code plus facile et plus rapide à écrire, modifier et comprendre
  • Avec son code intuitif, ses itérations rapides et sa faible charge mentale, Zig aide le développeur à se concentrer sur la résolution du problème plutôt que sur l’outil
  • Zig fait confiance au développeur et lui donne les bons outils ainsi que des choix, tandis que Rust peut sembler trop directif et restrictif
  • Au lieu de simplement « protéger le développeur de ses erreurs », un environnement favorable aux développeurs doit aussi lui garantir la possibilité d’apprendre et de progresser à travers ses propres erreurs

Conclusion

  • Dans les domaines où les forces de Rust brillent vraiment, comme les grands systèmes multithread et de longue durée d’exécution, Rust reste le meilleur choix
  • En revanche, pour de petits outils CLI concrets, la légèreté, la simplicité et la rapidité d’implémentation et de maintenance de Zig sont plus adaptées
  • La sûreté mémoire n’est qu’une pièce du puzzle de la sûreté ; des éléments essentiels aux outils CLI comme le comportement prévisible, la maintenabilité et la robustesse sont plus faciles à atteindre avec Zig
  • En fin de compte, ce qui compte n’est pas le « meilleur langage », mais le choix de l’outil adapté à mon workflow et aux caractéristiques du projet
  • Zig est un langage parfaitement adapté au développement de petits outils, en combinant « sûreté mémoire + faible coût cognitif + convivialité pour le développeur / productivité »

3 commentaires

 
shakespeares 2025-10-05

On dirait que l’écosystème n’est pas encore aussi stabilisé que celui de Rust.

 
bus710 2025-09-27

Comme Zig introduit très souvent des breaking changes dans ses nouvelles versions... même pour de petits projets, il vaut mieux mettre en place une CI et assurer un suivi continu.

 
GN⁺ 2025-09-27
Avis Hacker News
  • L’avantage de Zig, c’est qu’il permet de continuer à penser comme un développeur C, mais à un certain niveau, c’est aussi simplement une question d’habitude

    • Les développeurs suffisamment familiers avec Rust ne se battent plus avec le borrow checker, ils finissent par penser directement dans une structure de code compatible

    • Dans Rust, une approche du type « object soup » fonctionne mal, mais je ne pense pas que ce soit fondamentalement une méthode plus simple, c’est juste qu’elle paraît plus facile parce qu’on y est habitué

    • Si on admet que l’ergonomie est difficile à mesurer ou à quantifier, ce genre de débat reste forcément flou

      • C’est un peu comme dire : « Cette chaise ne va pas s’effondrer, tu peux t’asseoir sans crainte, même si elle est peut-être un peu moins confortable et plus lourde, la plupart des gens s’y habitueront vite et ne sentiront plus l’inconfort »
      • Comme le dit le billet d’origine, « Rust donne la sûreté mémoire au prix d’une expérience développeur moins agréable, tandis que Zig peut offrir une meilleure expérience développeur et la sûreté mémoire avec un peu d’attention », au fond c’est un échange entre sécurité et facilité d’usage
      • Je pense que la communauté Rust devrait reconnaître honnêtement ce compromis : obtenir plus de sûreté implique toujours moins de confort
      • C’est un principe qu’on retrouve dans la sécurité, la vie, le quotidien, et plus largement dans le logiciel, avec souvent des affirmations ambiguës ou subjectives
      • Certains écartent trop facilement le sujet de l’ergonomie en disant « les gens habitués n’ont pas de problème », ce qui peut donner l’impression de dire « si même ça te pose problème, c’est que tu es moins intelligent »
    • L’idée de « se battre avec le borrow checker » vient d’une époque où l’on ne comprenait que les lifetimes lexicaux dans Rust

      • Quand j’ai appris Rust en 2021, c’était déjà une vieille histoire
      • En pratique, pour quelqu’un qui n’a utilisé que Python, C ou JavaScript, s’adapter à Rust peut ne pas être facile
      • Dans mon cas, ça m’a semblé naturel, mais la plupart des gens n’ont pas l’air de le vivre ainsi
      • Cela dit, « lire les messages de diagnostic de Rust et corriger son code en conséquence » sonne moins héroïque que « se battre bravement avec le borrow checker », alors que c’est une description plus fidèle de la réalité
    • D’après mon expérience, les développeurs Rust expérimentés mettent des Arc un peu partout et s’en servent presque comme d’un garbage collection automatique

      • La gestion mémoire statique est assez stricte, donc pour des structures de données complexes, c’est difficile dans la pratique
      • À cause des lifetimes, les chemins de raisonnement à suivre dépassent parfois les capacités cognitives humaines
    • J’ai aussi vu beaucoup de projets open source en Rust, écrits par des développeurs chevronnés, qui utilisent Arc, Clone, Copy, etc. un peu partout

    • L’avantage de Zig, c’est qu’on peut développer de manière familière comme en C tout en bénéficiant de mécanismes de sûreté fournis par le langage et les outils

      • Par exemple, les optionals de Zig permettent d’éviter les problèmes de déréférencement de nil
      • En débogage ou en test, on peut facilement passer directement un allocateur debug/personnalisé pour faire des vérifications à l’exécution, contrôler les accès mémoire et détecter les fuites de ressources
      • L’absence d’interfaces/traits explicites me gêne un peu, mais l’adoption reste simple, donc je trouve ça pratique
  • Je ne suis globalement pas d’accord avec le billet d’origine

    • Rust, comme C ou Zig, oblige à réfléchir aux lifetimes, à l’ownership et à la portée des emprunts ; la différence, c’est l’aide du compilateur

    • L’être humain fait des erreurs, même s’il est intelligent, quand il est fatigué ou distrait ; reconnaître cela, c’est précisément faire preuve de sagesse

    • L’ensemble des programmes que le compilateur Rust considère sûrs n’est pas assez large, donc il rejette assez souvent des programmes pourtant valides

    • Exemple : si dans une struct Foo, bar et baz sont chacun des chaînes, alors prendre une référence mutable sur bar puis une référence immuable sur baz ne compile pas, et on est obligé de contourner artificiellement le problème en restructurant le code

    • Pour répondre à cela, devoir modifier son code vers une conception de second ou troisième choix juste pour éviter des cas « en réalité corrects mais refusés à la compilation » est déjà une charge importante

      • Cela peut finir par changer la conception de toute la base de code
      • C’est aussi ce qui explique pourquoi adopter Rust peut sembler pesant pour des gens qui font des choses difficiles, comme les développeurs de jeux
      • Si le compilateur Rust ne fonctionnait qu’avec une précision parfaite, sans faux positifs, il serait aussi extraordinaire du point de vue ergonomique, mais évidemment la réalité est plus compliquée
    • L’exemple ci-dessus me semble vraiment excellent ; j’aimerais demander si je peux en parler dans mon blog ou dans un article

    • En voyant ce code, je me sens au contraire encore moins sûr de l’argument

  • Il faut se rappeler que tous les programmes n’ont pas forcément besoin d’être aussi « sûrs »

    • Nous avons grandi en profitant de beaucoup de logiciels Unsafe : Star Fox 64, MS Paint, FruityLoops, etc.

    • J’ai lu qu’Andrew Kelley, le créateur de Zig, avait créé Zig parce qu’il n’existait pas d’environnement adapté au développement de logiciels de production musicale (DAW), et je pense que Zig convient bien à ce type de logiciel créatif

    • Si quelqu’un est très sensible aux bugs mémoire, il peut simplement utiliser Rust

    • Je crois même que Super Mario World était plus amusant à cause de ses bugs mémoire

    • « Sûr » est une manière abrégée de dire « mon programme fonctionne comme prévu »

      • Du code non logique et non intentionnel, une sorte de charabia sémantique, nuit à l’objectif recherché
      • Il existe des cas où l’on écrit volontairement du code artistiquement obscur (IOCCC, hacking, poésie de code), mais dans ce cas il faut le fabriquer avec soin
      • Même dans Rust, on peut implémenter volontairement ce genre de choses avec une porte de sortie (unsafe)
      • L’argument du texte revient à dire que coder accidentellement du non-sens est un avantage, et j’ai du mal à être d’accord avec ça
      • Si l’on pouvait rendre tout le code sûr sans aucun inconvénient, qui n’en voudrait pas ?
      • Les speedruns de Super Mario World, par exemple, pourraient aussi être obtenus via des patchs binaires ; je ne pense pas que manipuler la mémoire par les entrées soit l’unique source d’intérêt
    • Je suis un peu perdu : est-ce que le fait qu’on trouve mon avis mauvais signifie qu’on pense que la sûreté mémoire n’a pas d’importance ?

  • J’ai trouvé dommage que la valeur du borrow checker soit sous-estimée

    • Le borrow checker de Rust garantit à la compilation l’absence d’accès mémoire invalides

    • Bien sûr, cela a pour contrepartie l’inconfort de devoir réorganiser son code pour respecter les règles du compilateur

    • Quand j’utilise Rust de mon côté, je n’ai jamais eu l’impression que les annotations de lifetime étaient « fausses » ; c’était un peu fastidieux, mais on s’y habitue vite

    • Tant qu’on n’utilise pas unsafe, deux threads ne peuvent pas écrire simultanément dans la même mémoire en Rust

    • Je ne comprends pas vraiment l’argument « pourquoi Zig semble plus pratique pour les outils CLI » ; Rust garde un avantage clair pour éviter les CVE (vulnérabilités)

    • En pratique, je fais déjà la plupart de mes tâches dans des langages à GC, et quand je contribue dans d’autres langages, Rust, Zig ou C/C++ me conviennent tous

    • Un outil CLI n’aurait rien de spécial ?

      • En général, un outil CLI n’est pas énorme ou bien il est souvent développé en solo, donc c’est plus facile à gérer
      • Zig ou C ne conviennent pas toujours à une grosse base de code ; pour des projets plus complexes, on a besoin d’une sorte de babysitter
      • On a déjà eu le même débat autrefois avec Java, qualifié lui aussi de « langage babysitter », alors qu’en réalité beaucoup de bases de code ont justement besoin de ça
    • Dire que sans unsafe, deux threads ne peuvent pas écrire en même temps dans la même mémoire n’est pas aussi simple que ça

      • L’aide que j’attends surtout du compilateur concerne les problèmes de memory ordering
      • Rust classe ce genre de race comme safe et non comme unsafe, même lorsqu’un problème peut effectivement apparaître
  • Je suis d’accord sur le fait que les backlinks sont trop compliqués à implémenter en Rust

    • C’est possible avec Rc, Weak, RefCell, .borrow(), etc., mais ce n’est pas simple

    • Pour un programme de courte durée, l’allocation en arena est aussi une option, ce qui semble correspondre à ce qu’on entend par outil CLI

    • Rust donne vraiment le meilleur de lui-même dans les applications énormes, multithreadées et de longue durée

    • J’ai réellement écrit un gros client de métavers en Rust, avec des dizaines de threads tournant 24 h/24 sans fuites mémoire ni crash

    • Faire la même chose en C++ demanderait une équipe QA et des outils comme Valgrind, tandis qu’un langage de script serait beaucoup trop lent

    • J’ai aussi créé en Rust un avion de simulation physique qui tient compte jusqu’à la courbure de la Terre et aux variations de gravité

      • J’ai fait tourner pendant des années des tests de trajet de 5 heures et 22 heures sans aucun problème
      • En 7 ans avec Rust, je n’ai connu que très peu de crashs ; je ne touche au C/C++ que pour corriger du code hérité
  • Zig est séduisant, mais D existe toujours, et personnellement j’ai l’impression que D est davantage le remplaçant de C/C++ que je recherche

    • La syntaxe de Zig me paraît un peu étrange, et Rust est déjà devenu central dans l’écosystème

    • Go aussi occupe une place importante dans de nombreux outils, et dans le domaine de l’IA, c’est probablement le langage le plus utilisé après Python

    • Avant Rust, il y avait déjà le débat Go vs. D, et j’avais même acheté un manuel sur D avant de finalement passer à Go

      • Pour moi, la bibliothèque standard était bien plus pratique, et des noms de types comme int64 me semblaient plus intuitifs
    • D est bien, mais il n’a pas eu l’application phare qui lui permettrait de se démocratiser

  • Je ne vois pas très bien pourquoi il faudrait absolument utiliser Rust ou Zig pour faire des outils CLI

    • Le goulet d’étranglement, c’est l’I/O, pas le fait que le GC soit lent

    • Les questions de GC ne sont pas vraiment pertinentes hors des domaines gourmands en mémoire comme les jeux ou les bases de données

    • Je veux surtout souligner que, plus que le débat sur la sûreté mémoire, je réfléchis davantage aux raisons de choisir un langage sans GC

    • Si c’est simplement parce que « le no-GC, c’est amusant », alors cela suffit en soi et il n’y a pas besoin d’en débattre

    • Un temps de démarrage immédiat, sans latence à l’exécution, est très utile

      • Quand on crée un outil d’enrobage, pouvoir l’exécuter des milliers de fois sans friction est un vrai avantage
      • La distribution est aussi plus simple, car on évite la complexité de déploiement d’un environnement comme Python
    • Construire des CLI en Go m’a semblé très satisfaisant, même si je n’aime pas particulièrement le langage Go lui-même

      • Les CLI Python deviennent pénibles à distribuer quand il y a beaucoup de dépendances, et Rust/Zig sont aussi populaires parce qu’ils facilitent, comme Go, la distribution de binaires statiques
    • Je choisis en priorité des langages qui ont des sum types, du pattern matching et du support async

      • Même si ce n’est pas Rust, j’apprécie beaucoup les fonctionnalités qui permettent d’attraper certaines erreurs à la compilation
    • À propos de l’idée selon laquelle le développement sans GC ne concernerait que le jeu vidéo

      • En réalité, beaucoup de jeux mobiles utilisent aussi le GC, comme dans les environnements Unity + il2cpp, et les performances du GC n’y sont pas toujours bonnes
    • Le débat sur le GC a aussi un petit côté effet de mode

      • Il y a déjà 50 ans, de gros postes de travail avaient été construits avec succès en langages à GC comme Interlisp ou Cedar
      • Le matériel actuel est bien plus puissant que les CLI des années 1970 ou même que les applications Electron, mais on n’en tire pas toujours parti efficacement
  • J’ai créé un petit outil de notes avec les mécanismes intégrés de borrow/référencement de Rust, et ce n’était pas aussi complexe que je l’imaginais

    • Si l’on imagine une structure qui stocke l’index des notes dans une liste et les relie via une map, la différence de performance est quasi nulle et on ne perd rien en sûreté

    • Même en cas d’erreur d’index, on obtient au moins une erreur de dépassement de bornes, ce qui est bien préférable à l’écrasement de la mémoire du noyau

    • Même pour le printf debugging, c’est beaucoup plus simple et intuitif

    • Les raw pointers ou les références ne devraient en général être utilisés que là où c’est vraiment nécessaire, comme un allocator ou un runtime async ; pour la logique métier courante, une approche basée sur des index convient mieux

    • C’est d’ailleurs pour cela que l’async de Rust a notoirement des problèmes avec les self-referential structs et tout ce qui touche à Pin

    • Les pointeurs vers des valeurs stockées dans un vec deviennent invalides en cas de realloc, etc., et dans ce cas Miri signale immédiatement une erreur

  • En tant que développeur C++, si je cherchais un langage sûr, j’aurais tendance à penser que Swift est le plus adapté

    • On s’adapte plus vite à un langage qui nous est familier ou qui lui ressemble

    • Swift a récemment renforcé son support cross-platform, et plusieurs personnes actuellement impliquées dans le comité de standardisation C++ y participent

    • Mais ses liens avec Apple et l’absence de framework UI natif limitent encore un peu son expansion hors de l’écosystème Apple

    • J’aimerais que Swift gagne en popularité

    • Si quelqu’un a des ressources pour comparer Swift à Zig/C, je suis preneur

  • On dit que Zig permet aussi de développer des logiciels sûrs du point de vue mémoire avec un minimum de prudence, mais en réalité on peut dire la même chose de C si on l’utilise avec assez de discipline

    • Au final, ce « petit effort de discipline » manque souvent dans le monde réel, et c’est bien là que les problèmes apparaissent

    • Zig résout en plus les problèmes suivants

      • les accès hors limites (70 % de l’ensemble des CVE)
      • le déréférencement de pointeur nul
      • la sûreté de type
      • Zig est très supérieur à C, et même les erreurs comme le use-after-free sont bien plus faciles à éviter tant qu’on ne franchit pas certaines limites
      • Son excellent système de build cross-platform, les optimisations comptime et des temps de compilation des dizaines de fois plus rapides que C++/Rust sont aussi de vrais atouts
      • La bibliothèque standard est encore incomplète et il reste quelques petits problèmes, mais je pense que l’avenir est prometteur pour les programmes orientés performance
    • Si C échoue depuis plus de 50 ans sur cette question de discipline, c’est que c’est plus difficile que « la voie de Shaolin »