Réécrire Bun en Rust
(bun.com)- Bun, qui a démarré en Zig, est devenu un runtime téléchargé plus de 22 millions de fois par mois, mais l’interaction entre un moteur JavaScript à GC et une gestion manuelle de la mémoire a provoqué des problèmes de stabilité récurrents, servant de déclencheur au passage à Rust
- Plutôt que de faire migrer manuellement 535 496 lignes de code Zig par des humains pendant un an, l’équipe a exécuté en parallèle pendant 11 jours environ 50 workflows dynamiques de Claude Code et jusqu’à 64 instances de Claude
- Le portage a été validé avec
PORTING.md,LIFETIMES.tsv, 1 implémenteur et au moins 2 reviewers adversariaux, ainsi que la suite de tests TypeScript existante, et a réussi à 100 % sur la CI de 6 plateformes - Après le passage à Rust, Bun v1.4.0 corrige 128 bugs reproductibles dans v1.3.14, répare toutes les fuites mémoire instrumentables et réduit d’environ 20 % la taille des binaires Linux et Windows
- Bun v1.3.14 est la dernière version Zig et v1.4.0 la première version Rust, disponible en canary ; l’équipe utilise le borrow checker, Miri, LeakSanitizer et le fuzzing 24/7 basé sur la couverture comme outils d’amélioration de la stabilité
Bun, né en Zig, et les problèmes de stabilité
- Bun a commencé comme un projet de portage ligne par ligne de Go vers Zig du transpileur JavaScript et TypeScript d’esbuild
- Le premier code Zig a été écrit le 16 avril 2021, et le contrôle bas niveau ainsi que la conception orientée performances de Zig ont rendu possible l’implémentation initiale de Bun
- Les premières versions de Bun ont été écrites en Zig par une seule personne pendant un an, avec un périmètre très large
- Transpileur, minifieur et bundler JavaScript, TypeScript et CSS
- Gestionnaire de paquets compatible npm
- Test runner similaire à Jest
- Résolution de modules compatible Node.js et TypeScript
- Client HTTP/1.1 et WebSocket
- Implémentation d’API Node.js comme
fs,net,tls
- Aujourd’hui, le CLI Bun est téléchargé plus de 22 millions de fois par mois, Claude Code et OpenCode l’utilisent comme runtime, et Vercel, Railway, DigitalOcean, entre autres, proposent une prise en charge first-party
Bugs récurrents de sûreté mémoire
- Parmi les bugs corrigés dans Bun v1.3.14 figurent des use-after-free, double-free, fuites mémoire, accès out-of-bounds et race conditions
- Heap-use-after-free causé par l’appel de
.reset()pendant un.write()async denode:zlib - Use-after-free dans
node:http2, où un callback JS réentrant provoque un rehash de hashmap, invalidant un pointeur de stream interne - Problème dans
UDPSocket.send()etsendMany()où un callbackvalueOf()outoString()détache unArrayBuffer - Crashs et lectures out-of-bounds dans
Buffer#copyetBuffer#filldus au détachement ou au redimensionnement d’unArrayBufferpendant la coercition des arguments - Fuites mémoire liées à
crypto.scrypt,tlsSocket.setSession()etfs.watch() - Double-free dans le parser CSS lors du traitement des vendor prefixes et des arrière-plans multi-couches
- Crash par race condition sur
MessageEventlors d’accès concurrents àBroadcastChannelouMessagePort
- Heap-use-after-free causé par l’appel de
- Plusieurs dispositifs étaient déjà utilisés pour renforcer la stabilité
- Patch du compilateur Zig pour prendre en charge Address Sanitizer et exécution de la suite de tests ASAN à chaque commit
- Distribution de builds Windows ReleaseSafe avec vérifications de sûreté Zig
- Fuzzing 24/7 des API du runtime Bun avec Fuzzilli
- Exploitation de nombreux tests end-to-end de fuites mémoire
- La position n’est pas que Zig serait en soi le problème ; la principale source des problèmes de stabilité venait de l’obligation de manipuler ensemble des valeurs sous GC et de la mémoire gérée manuellement
Pourquoi Rust a été choisi
- JavaScript est un langage à GC, et les moteurs comme JavaScriptCore et V8 imposent des règles strictes concernant la gestion des exceptions et le GC
- Comme C, Zig ne gère pas automatiquement la mémoire ; il n’a ni constructeurs ni destructeurs, et le nettoyage doit le plus souvent être explicité avec
deferà chaque call site - Dans Bun, gérer correctement la lifetime des valeurs GC et des valeurs gérées manuellement constituait une source majeure de problèmes de stabilité
- Il fallait vérifier où les octets alloués étaient libérés
- Il fallait garantir qu’ils n’étaient libérés qu’une seule fois
- Il fallait vérifier correctement la gestion des exceptions JavaScript
- Il fallait s’assurer que les pointeurs GC étaient visibles par le scanner de pile conservatif
- Le mécanisme de cleanup de Zig repose sur
defereterrdeferexplicites, C++ utilise des destructeurs et le move, tandis que Rust utiliseDrop - Le code Zig existant de Bun combinait lifetimes d’arena, reference counting et revues minutieuses
- On peut faire appliquer des règles d’ownership par un guide de style et des code reviews, mais dans du code Rust sûr, les use-after-free, double-free et
freemanquants dans les chemins d’erreur deviennent des erreurs de compilation - Environ 20 % du code de Bun est en C++, et plusieurs bibliothèques C/C++ sont intégrées
- JavaScriptCore
- uWebSockets et usockets
- lshpack et lsquic
- BoringSSL
- SQLite
- C++ aurait aussi pu être une option, mais il reste dépendant des guides de style et des code reviews, et des corruptions mémoire ainsi que des fuites peuvent survenir même avec ASAN
Stratégie de réécriture : en une fois, mécaniquement
- Le code Zig existant de Bun comptait 535 496 lignes hors commentaires, et une réécriture traditionnelle était estimée à environ un an de travail pour une petite équipe d’ingénieurs
- Comme il n’était pas possible d’arrêter pendant un an les corrections de bugs, les correctifs de sécurité et le développement de fonctionnalités, un portage mécanique minimisant les changements de comportement utilisateur a été choisi comme approche la moins risquée
- La suite de tests de Bun étant écrite en TypeScript, elle ne dépendait pas du langage d’implémentation du runtime
- Une réécriture incrémentale aurait nécessité de créer du code temporaire en espérant le supprimer plus tard, ce qui a été jugé douloureux à court et moyen terme ; l’équipe a donc tout migré en une seule fois
- Le code Rust a été écrit pour ressembler à du code Zig transpilé, avec l’objectif, après Bun v1.4, de réduire progressivement
unsafeet de refactorer vers du Rust idiomatique
Workflows dynamiques de Claude Code
- Pour la réécriture en Rust, environ 50 workflows dynamiques ont été exécutés en continu dans Claude Code pendant 11 jours
- Les workflows allaient de la rédaction du guide de portage à la conversion de fichiers, la correction d’erreurs de compilation, la restauration de subcommands, la réussite de toute la suite de tests et de grands cleanups
- Génération d’un guide de portage mappant les patterns et types Zig vers des patterns et types Rust
- Portage mécanique de tous les fichiers
.zigen fichiers.rsconformément àPORTING.mdetLIFETIMES.tsv - Correction des erreurs du compilateur par crate
- Restauration du comportement de subcommands comme
bun testetbun build - Réussite de l’ensemble de la suite de tests
- Refactorings et cleanups à grande échelle
- Pendant l’essentiel de la période, des humains lisaient la sortie des workflows, identifiaient les problèmes et les bugs, puis ajustaient les prompts pour que Claude corrige les boucles
- En préparation, l’équipe a discuté pendant environ 3 heures avec Claude de la manière de mapper vers Rust les patterns de la base de code Zig, et le résultat a été sérialisé dans
PORTING.md - Pour ajouter des lifetimes Rust au code de gestion manuelle de la mémoire, un workflow analysant les lifetimes de tous les champs de structures a été exécuté
- Recherche des champs aux lifetimes complexes
- Proposition de lifetimes
- Revue par 2 agents reviewers adversariaux
- Intégration des retours et enregistrement dans
LIFETIMES.tsv
Méthode de revue contradictoire
- Pour chaque Claude chargé de l’implémentation, un Claude relecteur contradictoire était placé dans une fenêtre de contexte distincte ; le relecteur ne recevait que le diff et avait pour consigne de supposer que le code était incorrect afin d’y trouver des bugs
- La structure de base comprenait 1 implémenteur, au moins 2 relecteurs contradictoires et 1 fixer
- Tous les bugs réellement détectés par les relecteurs passaient la compilation, mais posaient des problèmes de comportement
- Comme
uv_closeest asynchrone, leBox<uv::Pipe>était drop à la fin du match arm, laissant libuv conserver de la mémoire libérée, ce qui provoquait un use-after-free et un double-free - Une erreur de timespec où l’utilisation de
trunc()sur un file time négatif non entier produisait unnsecnégatif - Une erreur où
unwrap_orévalue ses arguments de façon eager, provoquant un panic dans le cas où le pourcentage decolor-mix()est omis
- Comme
- Comme dans une revue humaine, les contextes de l’auteur et des relecteurs étaient séparés afin de réduire le biais pouvant venir du fait que l’implémenteur souhaite merger
Exécution du portage à grande échelle et parallélisation
- Avant de porter les 1 448 fichiers
.zig, la procédure a d’abord été validée sur 3 fichiers- 1 implémenteur écrivait le fichier
.rs - 2 relecteurs vérifiaient que le comportement correspondait au
.ziget quePORTING.mdetLIFETIMES.tsvétaient respectés - 1 fixer appliquait les suggestions
- 1 implémenteur écrivait le fichier
- Au début du portage complet, plusieurs Claude exécutaient
git stash,git stash popetgit reset HEAD --hard, provoquant des conflits entre eux - Des règles ont ensuite été ajoutées au workflow pour interdire
git stash,git reset, les commandesgitautres que le commit de fichiers précis, ainsi que les commandes lentes commecargo - Au final, 4 shards de workflow et 4 worktrees ont été utilisés, avec 16 Claude par shard qui committaient et poussaient des fichiers
- Grâce à la parallélisation et à la préparation en amont, au pic, Claude écrivait environ 1 300 lignes de code par minute
- Hors commit de merge, la branche de portage comptait 6 502 commits ; l’heure de pointe a atteint 695 commits, et le diff final landed représentait +1 009 272 lignes
- Il y a aussi eu un problème dû au fait que les IOPS par défaut de l’instance EC2 n’avaient pas été augmentés : un seul
greplent pouvait bloquer les lectures et écritures disque pendant plusieurs minutes
Erreurs de compilation et séparation en crates
- Une fois tout le code écrit, le workflow Claude a corrigé les erreurs du compilateur
- La base de code Zig était en pratique une seule compilation unit, tandis que le code Rust devait être divisé en environ 100 crates pour accélérer la compilation
- La catégorie d’erreurs la plus délicate était celle des dépendances circulaires
- La seule PR de séparation en crates juste avant la réécriture en Rust ne suffisait pas
- Un workflow séparé classait et consignait où placer le code ayant des dépendances circulaires
- Un autre workflow effectuait ensuite ce refactoring
- Après la résolution des dépendances circulaires, environ 16 000 erreurs du compilateur sont apparues
- Ces erreurs ont été traitées en parallèle par crate
- Exécution de
cargo checkdans chaque crate - Regroupement et enregistrement de la sortie par fichier
- Correction des erreurs de compilation du crate concerné
- Revue des changements par 2 relecteurs contradictoires
- Application des corrections par 1 fixer
- Exécution de
- Il y a aussi eu un faux départ où Claude a interprété « rendre tous les crates compilables » comme la création de stubs de fonctions
- Lorsqu’un schéma consistant à justifier des workarounds par de longs commentaires explicatifs est apparu, une règle de revue a été ajoutée : « si un commentaire de la longueur d’un paragraphe est nécessaire, le code est faux et il faut corriger le code »
Chemin jusqu’au passage des tests
- Après le passage de
cargo check, les problèmes ont été résolus dans l’ordre suivant : erreurs de linkage, panic immédiatement au démarrage,bun --version, puis exécution debun test <file> - Un workflow a été utilisé pour enregistrer dans des fichiers les stacktraces d’échec par subcommand CLI, puis les corriger via une boucle implémenteur/relecteurs/fixer
- Le workflow de fichiers de test shardait environ 100 fichiers de test aléatoires sur 4 worktrees, enregistrait les stacktraces et erreurs pour chaque échec, puis les corrigeait
- La suite de tests contenait des tests de fuite mémoire et des tests d’intégration pouvant timeout en debug build
- Un test qui exécute
next devet vérifie que le hot module reloading détecte 100 changements - Un stress test qui épuise le nombre maximal de sockets TCP
- Des tests de lectures/écritures disque de l’ordre du gigaoctet
- Un test qui spawn environ 10 000 processus
- Un test qui exécute
- Pour l’isolation,
systemd-runet les cgroups limitaient l’utilisation de la mémoire et du CPU et séparaient le pid namespace - Malgré cela, la machine a crashé plusieurs fois par manque d’espace disque
- Deux jours après la première exécution CI, le nombre de fichiers de test en échec est passé de 972 à 23 ; un jour et demi plus tard, Linux était entièrement green
- Au final, l’ensemble des tests CI est passé sur 6 plateformes
- macOS x64
- macOS arm64
- Linux x64
- Linux arm64
- Windows x64
- Windows arm64
- Après 100 % de tests réussis, des humains ont vérifié manuellement que les tests étaient réellement exécutés et non skippés, puis ont mergé
- Le moment du merge dans
mainn’était pas une versioned release : le niveau de confiance n’était pas encore suffisant pour une release, mais il l’était pour se consacrer pleinement à la rewrite
Taille des tests et coût
- En 11 jours, du 3 mai au merge du 14 mai, 6 778 commits ont été créés
- Les tests n’ont pas été supprimés ni skippés
- La taille des tests par plateforme était la suivante
- Debian 13 x64 :
expect()1 386 826 fois, 60 624 tests, 4 174 fichiers - macOS 14 arm64 :
expect()1 259 953 fois, 58 850 tests, 4 175 fichiers - Windows 2019 x64 :
expect()1 007 544 fois, 57 337 tests, 4 173 fichiers
- Debian 13 x64 :
- Le travail pré-merge a consommé 5,9 milliards de tokens d’entrée non mis en cache, 690 millions de tokens de sortie et 72 milliards de lectures de tokens d’entrée mis en cache
- Au tarif API, le coût était d’environ 165 000 dollars
- L’estimation est que, fait manuellement, le travail aurait pris environ un an à 3 ingénieurs ayant le contexte complet de la codebase
- Le modèle utilisé était la préversion de Claude Fable 5, avec une disclosure indiquant que Bun a été acquis par Anthropic en décembre 2025
Revue de sécurité, fuzzing et état de l’unsafe
- Après le merge du port Rust, 11 rounds de revue de sécurité ont été menés avec Claude Code Security, et les findings ont été traités
- Du fuzzing basé sur la couverture, 24/7, a été ajouté pour tous les parsers de Bun
- JavaScript
- TypeScript
- JSX
- CSS
- JSON5
- JSONC
- TOML
- YAML
- Markdown
- INI
- scripts Bun Shell
- plages semver
- fichiers
.patch - couleurs CSS
- Le fuzzer envoie les bugs trouvés à Claude afin qu’il soumette des PR incluant reproduction et correctif ; les humains reviewent ensuite les PR
- À ce jour, les parsers ont été exécutés 100 milliards de fois, ce qui a donné lieu à environ 15 PR
- Au moment de la rédaction, environ 4 % du code Rust se trouve dans des blocs
unsafe- Environ 13 000 mots-clés
unsafe - Environ 27 000 lignes / environ 780 000 lignes au total
- 78 % des blocs
unsafetiennent sur une seule ligne et correspondent à des pointeurs venus de C++ ou à des appels à des bibliothèques C
- Environ 13 000 mots-clés
- Étant donné que Bun continue d’utiliser des bibliothèques C/C++ comme JavaScriptCore, il est indiqué que la quantité d’
unsaferestera toujours supérieure à celle d’un projet Rust pur
Régressions découvertes après le passage à Rust
- La réécriture en Rust était un changement de grande ampleur et a créé 19 régressions connues, toutes corrigées
- La plupart provenaient de code dont la syntaxe est similaire dans les deux langages, mais dont la sémantique diffère
-
Effets de bord dans
debug_assert!- En Zig,
assertest une fonction, donc ses arguments sont exécutés dans tous les builds - En Rust,
debug_assert!est une macro, donc toute l’expression est supprimée dans les builds de release - L’appel à
insert_staledisparaissait dans les builds de release, ce qui cassait un cas HMR précis dans les projets de routes HTML utilisant React - Issue liée : #30678
- En Zig,
-
Slices de longueur impaire
- Le helper Zig de Bun
reinterpretSlice(u16, bytes)utilisait@divTruncet ignorait l’octet impair final - En Rust,
bytemuck::cast_slicepanique sur une longueur impaire - Il existait une régression où
Blob.text()avec un nombre impair d’octets après un BOM UTF-16 faisait paniquer le process au lieu de retourner une chaîne - Le correctif consiste à ignorer de nouveau l’octet impair avec
&buf[..buf.len() & !1] - Issue liée : #31188
- Le helper Zig de Bun
-
Bounds checks
- Le code Zig pour macOS et Linux était compilé en
ReleaseFast, ce qui supprimait les bounds checks, tandis que les builds Rust de release les conservent - La taille du bloc d’overflow du resolver de modules de Bun était restée sur la valeur placeholder
64, abaissant le plafond de 8,4 millions de noms de fichiers internés à 270 272 - Le off-by-one
ptrs[4095]porté devenait atteignable dans des projets réels, et Rust déclenchait une panique au lieu d’une écriture hors limites - Issue liée : #31503
- Le code Zig pour macOS et Linux était compilé en
-
Chaînes de format
comptime- Dans
Output.prettyde Zig,fmtétantcomptime, les marqueurs de couleur<r>et<d>étaient convertis en séquences d’échappement ANSI avant la substitution des arguments - La fonction Rust n’ayant pas de paramètre comptime, elle traitait les marqueurs dans la chaîne finale et réécrivait aussi les arguments à tort
- Dans
bun update -i, la terminaison des hyperlinks OSC 8 et le marqueur final<r>entraient en conflit, ce qui affichaitrcomme du texte - En Rust, il fallait utiliser la macro
bun_core::pretty!("<r>{}<r>", hyperlink) - Issue liée : #30693
- Dans
Bugs corrigés et fuites mémoire
- Bun v1.4.0 corrige 128 bugs reproductibles dans v1.3.14
- Le périmètre va des fuites mémoire et crashs jusqu’au texte d’aide mal coloré
- En Rust,
Dropappelle automatiquement la fonctiondroplorsqu’une valeur sort de son scope - En Zig, il fallait ajouter
deferà chaque call site, ce qui facilitait les oublis de cleanup ou les cleanups en double Dropen Rust est un choix qui réduit les footguns courants, au prix de l’acceptation d’un control flow cachéDropa corrigé plusieurs fuites mémoire liées aux chemins de fichiers dans le code de gestion des erreurs- L’intégration de LeakSanitizer dans Bun a été améliorée afin de suivre toutes les allocations mémoire du code natif
- Toutes les fuites mémoire instrumentables ont été corrigées
-
Amélioration des fuites de
Bun.build()- Dans l’ancien Bun v1.3.14, à chaque appel in-process de
Bun.build(), le texte source parsé et la table de symboles de l’AST survivaient à la durée de vie du build, entraînant des fuites de plusieurs Mo - Dans un test bundlant 2 000 fois le même projet de 60 modules dans un seul process, v1.3.14 continuait à fuir environ 3 Mo à chaque build
- Dans Bun v1.4.0, l’utilisation mémoire se stabilise
- | Builds | Bun v1.3.14 | Bun v1.4.0 |
- | --- | ---: | ---: |
- | 500 | 1 914 MB | 526 MB |
- | 1 000 | 3 506 MB | 586 MB |
- | 1 500 | 5 097 MB | 608 MB |
- | 2 000 | 6 745 MB | 609 MB |
- Dans l’ancien Bun v1.3.14, à chaque appel in-process de
Taille du binaire, utilisation de la pile et performances
- Les premiers changements de la réécriture en Rust ont à eux seuls réduit la taille du binaire
- Windows : réduction de 3,8 MB
- macOS : réduction de 5,5 MB
- Linux : réduction de 6,8 MB
- La principale cause était l’usage excessif de
comptimedans le code Zig - Par la suite, l’Identical Code Folding, la suppression des données ICU inutilisées et la décompression différée de parties de libicu avec un dictionnaire zstd ont également été appliqués
- En combinant la réécriture en Rust, les changements ICU et l’Identical Code Folding, la taille du binaire Bun diminue d’environ 20 % sous Linux et Windows
| Version | Plateforme | Taille |
|---|---|---|
| Bun v1.4.0 canary | Windows | 76 MB |
| Bun v1.3.14 | Windows | 94 MB |
| Bun v1.4.0 canary | Linux | 70 MB |
| Bun v1.3.14 | Linux | 88 MB |
- Le parser TOML et les parsers recursive-descent de Bun utilisent moins d’espace de pile
- La génération de code LLVM IR de Rust émet les intrinsèques
llvm.lifetime.startetllvm.lifetime.endpour les variables de pile, ce qui permet à LLVM de réutiliser les emplacements de pile - Auparavant, il fallait refactorer manuellement les fonctions particulièrement grandes en plusieurs petites fonctions pour contourner une issue ouverte de Zig
- Rust prend en charge la link-time optimization cross-language entre C/C++ et Rust, ce qui permet l’inlining entre langages
-
Benchmarks Linux x64
- Bun v1.3.14 et Bun v1.4.0 ont été comparés sur Linux x64 avec une EC2 Xeon Platinum 8488C
- Le débit HTTP a été mesuré avec oha, et les workloads applicatifs avec hyperfine
- | server | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | Bun.serve | 169,6k req/s | 177,7k req/s | +4,8 % |
- | node:http | 103,8k req/s | 108,5k req/s | +4,5 % |
- | Elysia | 158,9k req/s | 163,3k req/s | +2,8 % |
- | express | 64,5k req/s | 66,6k req/s | +3,2 % |
- | fastify | 91,5k req/s | 95,9k req/s | +4,8 % |
- | workload | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | next build | 13,62 s | 13,03 s | +4,5 % |
- | vite build | 1,69 s | 1,65 s | +2,2 % |
- |
tsc -b --force| 0,94 s | 0,89 s | +4,7 % |
Cas d’usage réels et état des versions
- Prisma a lancé la bêta publique de Prisma Compute sur la réécriture de Bun en Rust
- Prisma indique avoir testé, dans la réécriture en Rust, des modes de défaillance avec un connection pool ne se rétablissant pas après un pause/resume de VM et des fuites mémoire, et que ces modes de défaillance ont été correctement gérés
- Claude Code v2.1.181, ainsi que les versions publiées après le 17 juin, utilisent le portage de Bun en Rust
- Le démarrage de Claude Code sous Linux est devenu 10 % plus rapide, mais à part cela, la plupart des utilisateurs ne l’auraient presque pas remarqué
- Bun v1.3.14 est la dernière version de Bun écrite en Zig
- Bun v1.4.0 est la première version de Bun écrite en Rust et est disponible en canary
Les outils obtenus par l’équipe et le travail restant
- La nouvelle base de code Rust conserve une forme très proche de l’ancienne base de code Zig
- Elle a été écrite de sorte qu’une personne comprenant le code Zig d’origine puisse aussi comprendre le code Rust traduit mécaniquement
- La revue de la PR de réécriture en Rust a consisté à vérifier qu’un agent de revue contradictoire repérait correctement les incohérences entre Zig et Rust, ainsi que le respect du guide de portage et du guide des lifetimes, puis à faire lire par des humains une grande quantité de code côte à côte
- Bun v1.4 rend Bun plus rapide et plus compact, réduit son utilisation mémoire et fournit des outils pour améliorer sa stabilité
- Rust borrow checker
- Miri
- LeakSanitizer
- fuzzing coverage-guided 24/7 ciblant le parser
- Il reste encore des parties à refactorer, et bun-unsafe-audit est intégré
- Un ingénieur a surveillé de près Fable et Claude Code, permettant d’atteindre en seulement 11 jours un état où toute la suite de tests passait sur toutes les plateformes
1 commentaires
Avis sur Lobste.rs
extern "C", mais ils auraient quand même dû s’appuyer sur un guide de style imposé par la revue de code, et même avec ASAN, il y aurait toujours eu des corruptions mémoire et des fuites.Ce qui est amusant, c’est que Node.js fonctionne très bien en C++, mais je n’ai jamais vraiment considéré Bun comme un projet sérieux. Maintenant, il ressemble à un banc d’essai pour le département marketing d’Anthropic, donc je compte continuer à m’en tenir éloigné.
Spoiler : en réalité, on ne fait pas autant attention, et on se trompe.
unsafe, et 78 % de ceux-ci ne font qu’une ligne » semble formulée pour rassurer, mais le fait qu’un blocunsafefasse une ligne ou non n’a pas d’importance. Si l’on y casse les garanties de sûreté, tout le code en dehors du bloc peut lui aussi potentiellement devenir non sound.La fusion initiale du port Rust de Bun contenait une unsoundness évidente de ce genre : https://github.com/oven-sh/bun/issues/30719
Le problème a été pris en compte par les mainteneurs en activant l’outil Miri de Rust dans la CI, et la section « What’s Next » de l’article mentionne aussi Miri (which runs for a growing chunk of code in CI), donc il est encourageant de voir qu’ils travaillent dans cette direction.
Pour être juste, même du Rust comportant des violations de sûreté peut être plus facile à maintenir selon la qualité du code Zig qu’il remplace. Mais le nombre de lignes par bloc unsafe n’est pas un indicateur de qualité, surtout si ces blocs ne sont pas accompagnés d’autres pratiques de codage, d’expertise ou de vérifications automatisées.
unsafene viennent pas du processus de portage, mais plutôt des exigences du projet. Si l’on appelle une bibliothèque C, les blocsunsafesont nécessaires, et on ne peut pas les faire disparaître par un simple refactoring.Bien sûr, ce serait possible en réécrivant aussi cette bibliothèque C, mais cela pourra éventuellement être envisagé plus tard.
Dans l’annonce « Bun is joining Anthropic », Jarred disait qu’ils recruteraient davantage d’ingénieurs pour travailler sur Bun, mais à en juger par GitHub, l’équipe Bun semble plutôt avoir diminué. Je ne sais pas trop quelle conclusion en tirer, sinon que « Bun est une petite équipe ».
La méthode elle-même est intéressante, mais l’article se lit comme un papier marketing. Il manque aussi une analyse du coût, il n’y a pas vraiment de risques associés à la réécriture en Rust, et l’explication concrète de la raison pour laquelle cette réécriture a eu lieu est faible. On pourrait supposer que c’est à cause de la politique no ai de Zig, ou bien d’une volonté interne d’Anthropic de se concentrer sur Rust.
Et sur la raison de la réécriture, je trouve que l’article raconte une histoire assez cohérente. Bun continuait de crasher malgré les mesures prises pour traquer les problèmes, et les développeurs voulaient une manière plus systématique d’empêcher ce genre de problèmes.
Le plan initial consistait à imposer plus strictement un certain style de codage et à introduire des smart pointers, mais Jared estimait que leurs smart pointers maison seraient moins ergonomiques que Rust et n’apporteraient pas de garanties. C’est ainsi que l’idée est devenue : « et si on testait pendant une semaine si le nouveau modèle d’Anthropic peut réécrire Bun en Rust ? ». À mesure que le taux de réussite de la suite de tests augmentait, on semble être passé de « ça vaut peut-être le coup » à « on va fusionner ».
Autrement dit, il ne semble pas qu’ils aient décidé dès le départ de réécrire en Rust ; cela ressemble plutôt à : « Rust semble apporter une solution au problème, mais le coût d’une réécriture nous en empêche. Or un portage par LLM montre que c’est peut-être faisable. Donc allons vers une réécriture par LLM ».
Félicitations à Jarred, à l’équipe Bun et à Anthropic pour avoir réussi cela.