Zig vs Rust en 2026
(zackoverflow.dev)- Il y a environ 3 ans, lorsque l’auteur a implémenté lui-même une VM à bytecode et un garbage collector en Zig et en Rust unsafe, l’ergonomie plus humaine de Zig prenait l’avantage, mais à l’ère des agents de codage, cet avantage est devenu pratiquement insignifiant
- Le gain de productivité développeur de 1,5 à 5x apporté par les fonctionnalités majeures de Zig est écrasé par le gain de productivité de 100x offert par les agents de codage basés sur Rust
- L’interface d’allocator de Zig, les entiers à largeur de bits arbitraire, les
packed struct, comptime et d’autres fonctions clés brillent surtout quand un humain écrit directement le code - Le système de types de Rust est plus efficace pour empêcher à la compilation les erreurs des agents grâce au bounded polymorphism et à l’application d’invariants
- Dans une situation où le volume de code généré par les agents a été multiplié par 100, les garanties de sûreté mémoire de Rust deviennent un avantage décisif face à Zig
Changement fondamental
- Zig avait un gros avantage en matière d’ergonomie du code unsafe, mais à mesure que la part de code écrite directement par des humains diminue, la valeur pratique de cet avantage diminue aussi
- Le gain de productivité de 1,5 à 5x pour les développeurs humains apporté par les fonctionnalités de Zig est éclipsé par le gain de 100x avec des agents de codage en Rust
- Une grande partie des fonctionnalités emblématiques de Zig améliore surtout la commodité de l’écriture manuelle du code, mais cette différence compte beaucoup moins pour les agents de codage
- Il y a environ 3 ans, en écrivant une VM à bytecode et un garbage collector en Zig et en Rust unsafe, l’auteur avait eu le sentiment que l’expérience d’écriture de code unsafe était meilleure côté Zig
- Même en 2026, Zig reste un bon langage, mais Rust est devenu le langage le plus préféré et celui qui s’accorde le mieux avec les agents de codage
L’interface d’allocator de Zig
- L’interface d’allocator de Zig permet d’appliquer facilement des allocators spécialisés, comme arena ou stack fallback, afin d’optimiser des chemins de code précis
- Lorsqu’on lit une ligne d’entrée utilisateur, la longueur de l’entrée est théoriquement illimitée et nécessite donc un allocator sur le heap, mais en pratique l’entrée est souvent un mot-clé de recherche ou un chemin, donc bien inférieure à 1 KB
std.heap.stackFallback(256, heap_allocator)place un buffer de taille fixe sur la pile, puis ne bascule sur le heap qu’en cas de débordement, ce qui permet de traiter le cas général sans allocation sur le heap- Par le passé, Rust ne proposait pas d’équivalent à l’interface
Allocatorde Zig ; si l’on voulait unVec<T>avec allocator personnalisé, il fallait copier l’implémentation de la bibliothèque standard puis la modifier - Le code source des collections de Bumpalo consistait à forker les collections standard pour les brancher sur un bump allocator
- Rust nightly a longtemps proposé un trait
Allocator, et il semble désormais être arrivé à un niveau suffisamment bon - L’
Allocatorde Rust repose sur des traits et utilise donc le dispatch statique, contrairement aux allocators de Zig qui reposent sur des vtables - Rust n’a pas de convention communautaire généralisée consistant à concevoir les structures de données autour de paramètres d’allocator comme en Zig, mais cette limite compte moins maintenant que l’IA peut facilement copier et adapter du code
Entiers à largeur de bits arbitraire et packed struct
- Les entiers à largeur de bits arbitraire de Zig et les
packed structfacilitent des tâches comme l’optimisation du cache CPU dans une approche orientée données, les tagged pointers, le NaN boxing ou les bitflags - Lorsqu’on utilise une API Obj-C avec Metal via l’API C du runtime Objective-C,
idpeut être un tagged pointer et non un pointeur d’objet alloué sur le heap et aligné - Si l’on passe un
NSNumbertaggé à un code qui suppose un alignement, cela peut provoquer de l’UB ; il faut donc pouvoir vérifier à faible coût si l’on a affaire à « un pointeur heap ou un immédiat taggé » - Dans une disposition simplifiée des tagged pointers Objective-C, le bit de poids faible indique « pas un pointeur heap », les 3 bits suivants identifient le slot de classe, et les 60 bits restants constituent la payload
- Zig permet d’exprimer directement la disposition binaire comme type avec
enum(u3)etpacked struct, par exempleclass: TaggedClass,payload: u60 - En Zig,
@bitCastpermet d’aller et venir entre unu64brut etObjcTaggedPointer, et dansis_ns_number, on peut vérifieris_taggedainsi que la classe.ns_number - Le code équivalent en Rust place des constantes comme
TAG_MASK,CLASS_MASK,CLASS_SHIFTetPAYLOAD_SHIFTdansObjcTaggedPointer(u64), fait des OR à la création et applique des masques à l’accès - En Rust, le slot de classe n’est pas un vrai type mais une constante
u64, et cette manière d’écrire à la main est moins ergonomique qu’en Zig - En Rust, il vaut mieux utiliser des crates comme bitfield ou bitflags, mais elles dépendent toutes deux de proc macros et ne semblent pas aussi satisfaisantes que les
packed structde Zig - Avec des agents de codage, le problème du caractère fastidieux de l’écriture manuelle de ce type de code diminue fortement
L’évolution de la valeur de comptime
- comptime est la fonctionnalité la plus spectaculaire de Zig, et en dehors de quelques langages ésotériques à types dépendants, il existe peu de langages offrant une évaluation à la compilation aussi bonne que celle de Zig
- En pratique, cette fonctionnalité a fini par moins manquer, et environ 95 % de son usage servait à créer des structures de données génériques paramétrées par type
- Le motif typique est quelque chose comme
fn ArrayList(comptime T: type) type, qui reçoit un type et renvoie un type de struct contenantitems: []T,capacity: usize,allocator: Allocator - Le système de types de Rust remplace une grande partie des génériques à la Zig basés sur comptime et permet d’imposer davantage d’invariants
- Dans les 5 % de cas restants, l’absence de comptime est gênante, et il n’existe pas d’alternative fiable en dehors de la génération de code
- Lorsqu’on veut intégrer en dur dans une structure de données des données de hitbox geometry générées par des outils pendant le développement d’un jeu, en Rust il faut demander à Claude d’écrire un script qui génère un fichier Rust
- Malgré cela, on n’a pas si souvent besoin d’évaluation à la compilation en pratique
Les avantages du système de types de Rust
- Le système de types de Rust est jugé comme un échange plus précieux que le comptime de Zig, en particulier parce qu’il est fort dans la zone des traits/typeclasses pour le bounded polymorphism
- Tenter d’implémenter en Zig un bounded polymorphism du même niveau est extrêmement difficile
- Le système de types de Rust permet d’imposer davantage d’invariants, ce qui aide à empêcher les erreurs fréquentes des agents de codage
- Dans du code de jeu, on utilise la crate euclid pour éviter la confusion entre espaces de coordonnées, un problème fréquent en programmation graphique
- En créant des types spécialisés pour chaque espace de coordonnées, comme
Point<Screen>ouPoint<World>, on empêche à la compilation de mélanger par erreur les coordonnées monde et écran - Si l’on définit
WorldPoint,WorldVectoretScreenPointcomme types distincts, l’addition d’un point et d’un vecteur du même espace reste autorisée Translation2D::<f32, WorldSpace, ScreenSpace>permet de convertir explicitement de l’espace monde à l’espace écran- À l’inverse, un code comme
let bad: ScreenPoint = player;, qui assigne directement unWorldPointà unScreenPoint, n’est pas autorisé
L’effet de devoir moins traiter les problèmes mémoire
- Si les agents de codage permettent d’écrire 100 fois plus de code, alors la quantité de problèmes mémoire à examiner dans du code Zig augmente elle aussi d’un facteur 100
- Sans vérification formelle, la surface de l’espace de recherche à explorer pour trouver des bugs devient beaucoup plus grande
- Dans la situation actuelle, où le volume de code généré a fortement augmenté, Rust devient plus attractif
- Le compromis traditionnel de Rust était qu’il nuisait à la productivité des développeurs peu familiers avec le borrow checker, mais avec des agents de codage, l’importance de cet inconvénient diminue fortement
- Même lorsqu’on utilise
unsafeen Rust, on peut faire exécuter à un agent de codage des outils comme miri pour vérifier qu’il n’y a pas d’UB et qu’on ne viole pas les règles d’aliasing de Rust
Conclusion
- Zig reste un langage qui manque à l’auteur et demeure un bon langage
- Dans la manière de travailler de 2026, Rust est davantage préféré, et son adéquation avec les agents de codage est également meilleure
1 commentaires
Avis sur Lobste.rs
Mon ancien lead d’équipe avait une conviction assez forte : le copier-coller de code n’est pas toujours une mauvaise chose
À cause du principe DRY, ça paraissait instinctivement faux ou au moins polémique, mais c’était quelqu’un de très pragmatique, et il appliquait surtout cette idée à de grosses bases de code de test
Son raisonnement était qu’au lieu de forcer une interface partagée trop astucieuse, une base de code simple, mais plus grosse et plus redondante, peut être plus facile à maintenir
Avec les LLM ces temps-ci, je reviens à la même idée, et je l’applique maintenant à des parties plus importantes du logiciel
La génération de code est rapide, et les LLM semblent aussi avoir plus de chances de bien s’en sortir sur une base de code simple mais redondante
Si on met trop d’abstractions dans les tests pour réduire la duplication, ils deviennent plus difficiles à comprendre, avec en plus le risque d’être subtilement faux
Pire encore, si on réutilise les abstractions du code testé, les tests peuvent se tromper de la même manière que ce code
Et contrairement au code applicatif, les tests sont en pratique « composés » gratuitement
Tant qu’on n’a pas gravement saboté le harnais de test, on peut ajouter ou supprimer des tests à volonté sans affecter les autres, et comme il n’y a pas de friction d’intégration, cela fait une raison de moins d’éviter la duplication
Dans les tests, j’ai déjà vu ça formulé comme DAMP : « Descriptive and Meaningful Phrases », un principe qui met l’accent sur la lisibilité plutôt que sur l’unicité
Ce principe peut créer de la duplication en répétant du code similaire, mais il rend aussi les tests plus manifestement corrects
https://testing.googleblog.com/2019/12/…
Il existe une idée similaire dans la communauté Go : « un petit copier-coller vaut mieux qu’une petite dépendance » https://go-proverbs.github.io/
Je lui suis reconnaissant de partager ses sessions de live coding
Si je me souviens bien, quand il commençait quelque chose, il allait souvent chercher le code le plus proche de ce qu’il voulait construire, le copiait en entier, puis le modifiait à partir de là
Je me disais : « il ne va pas s’asseoir un moment pour réfléchir à l’abstraction commune entre les deux ? », mais il avançait simplement au copier-coller et était bien plus productif que moi
C’est intéressant quand on pense au timing de la réécriture IA de Bun de Zig vers Rust https://xcancel.com/jarredsumner/status/2053063524826620129#m
Parmi elles, 75 n’auraient pas compilé dans un langage avec destructeurs, sémantique de déplacement et borrow checker
En gros, une PR déployée sur trois revenait à « on a oublié de libérer quelque chose sur un chemin d’erreur »
Sur ces 108, environ 88 se trouvent côté Zig, et les quelque 14 côté C++ relèvent surtout de catégories résiduelles qui subsistent dans n’importe quel langage, comme les cycles de références et les races de concurrence dans le GC
Donc l’écart Zig→Rust est réel, et les bugs Zig sont précisément du type que destructeurs et ownership corrigent, tandis que le côté C++ est déjà proche du plancher
Sans garanties plus fortes à la compilation, ça restera un jeu du chat et de la souris
L’idée est de ne pas continuer à corriger individuellement la plus grosse catégorie de bugs, mais de l’éliminer structurellement
– bun/docs/rust-rewrite-plan.md at claude/phase-a-port · oven-sh/bun · GitHub
Le passage disant que « dans les 5 % restants, l’absence de comptime est pénible, et que la seule façon d’arriver de manière fiable à un résultat équivalent est la génération de code » n’est pas très clair sur ce que l’auteur veut dire
Parce qu’il ne dit rien des macros procédurales
C’est un peu pénible à bien concevoir, mais on peut faire énormément de choses avec
J’ai aussi l’impression que la génération de code a une mauvaise réputation un peu imméritée
J’ai résolu pas mal de problèmes pénibles via génération de code avec des scripts
build.rs, et ça fonctionne bienBien sûr, je le regretterai peut-être plus tard
La thèse centrale du texte me semble être à peu près la suivante
Rust est certes un bon langage, mais là ça me paraît quand même excessif
On dirait une pub pour des agents de code
Cela vient d’un billet lié du même auteur : en programmation graphique, une erreur très fréquente est de confondre les espaces de coordonnées, et le système de types est assez puissant pour exprimer quels espaces et quelles transformations sont valides
On peut dire la même chose des devises, des distances et poids en unités SI versus unités impériales, des chaînes validées versus chaînes fournies par l’utilisateur, et des secrets
De même, si on les gère bien, les types d’état peuvent empêcher des états impossibles ou non sound
Mais personnellement, la fonctionnalité que je veux le plus en Rust, c’est l’élimination complète des data races
Les langages managés ont eux aussi des data races
Le « utilise simplement Go » : dans Go, tout est mutable par référence entre threads, avec en prime la gymnastique des slices
Même JavaScript, qui se trouvait autrefois du côté le plus pur et le plus sûr et passait pour un langage complètement jouet, fait de chaque
awaitune course potentielleSans même parler du caractère maléfique du modèle everything-is-an-EventEmitter
Donc oui. S’il n’y avait que le GC… 🤫
J’ai l’impression que ça cache un peu l’essentiel
Les agents de code sont aussi très bons en Python et JavaScript
Dire qu’ils sont meilleurs qu’en Rust relève un peu du geste vague subjectif, mais ça ne veut pas dire que je choisirais ces langages pour beaucoup de travaux
Je me demande si le problème vient du fait que les fonctionnalités de Zig changent souvent, ou si c’est simplement parce que c’est un langage plus récent et que les données d’entraînement de l’IA y sont plus brouillonnes
J’ai l’impression que Zig est plus difficile à écrire que Rust, mais plus facile à lire
À l’ère de l’IA, on lit plus de code qu’on n’en écrit, donc j’ai tendance à préférer Zig
Vu le volume de code généré aujourd’hui, dire que Rust est plus attractif n’est qu’une première étape
Plus les ordinateurs écriront de code, plus les langages formels seront avantagés
Cela ressemble à une nouvelle étape dans le débat sur le typage dynamique
Du genre : « le typage dynamique est plus simple pour les humains, alors pourquoi faut-il préciser la même chose trois fois pour les machines ? »
Les types, les durées de vie… quoi d’autre pourrait être plus facile à écrire et à consommer pour les machines ?
Je me demande jusqu’à quel point il deviendra difficile pour les humains de coder directement dans les langages que les ordinateurs utiliseront pour écrire du code