Si Rust était réécrit
(josephg.com)- Rust part d’un constat critique : malgré ses atouts — sécurité mémoire, performances, types algébriques et Cargo — l’évolution du langage sur le canal stable ralentit, ce qui donne l’impression d’un « produit de première génération » encore inachevé
- Après l’agrandissement de la communauté, la prise de décision par consensus est devenue un goulot d’étranglement ; des fonctionnalités comme les coroutines, bien qu’implémentées, s’accumulent sans pouvoir être utilisées pendant longtemps dans stable Rust
- L’hypothétique « seph edition » viserait à conserver l’écosystème Rust et Cargo existant tout en autorisant des breaking changes afin d’expérimenter un système d’effets, des permissions à la compilation, l’emprunt de champs de structures, le
comptime, etc. - Pour réduire les risques de chaîne d’approvisionnement, il faudrait associer des capabilities aux fonctionnalités sensibles comme l’écriture de fichiers, le réseau, la FFI ou
unsafe, et demander à l’appelant de les autoriser explicitement - La plupart des propositions ne sont pas compatibles avec Rust actuel ; même l’ajout des seules capabilities créerait de nouvelles conditions d’échec rompant la compatibilité semver, ce qui nécessiterait une nouvelle edition ou un fork du compilateur
Pourquoi Rust donne l’impression d’un « produit de première génération »
- Au départ, Rust apparaît comme un langage très séduisant grâce à ses types algébriques, à sa sécurité mémoire sans perte de performances et à son gestionnaire de paquets moderne
- Après environ quatre ans d’utilisation, le langage donne l’impression de rester « toujours un peu incomplet »
- Le grief central est que les nouvelles fonctionnalités arrivent moins souvent dans stable Rust et que le rythme d’évolution du langage a fortement ralenti
- Le Rust unstable book recense environ 700 fonctionnalités instables, dont une grande partie concerne des changements de la bibliothèque standard
- Les coroutines sont une fonctionnalité dont la RFC date de sept ans et qui est implémentée dans le compilateur, mais que les utilisateurs de stable Rust ne peuvent toujours pas employer
- Le processus RFC de Rust ressemble à un espace où les bonnes idées restent longtemps en suspens
- La discussion sur l’amélioration de
Mutexest citée comme exemple : 25 personnes y ont laissé plus de 200 commentaires pendant deux ans, sans parvenir à une conclusion claire
- La discussion sur l’amélioration de
L’hypothétique « seph edition »
- L’idée imaginée consiste à forker le compilateur tout en laissant le code Rust existant intact, et à ajouter une Rust edition distincte appelée « seph »
- Dans cette edition, les breaking changes seraient autorisés, mais tant que le compilateur pourrait continuer à compiler le Rust mainline, les crates existantes de Cargo resteraient utilisables
- Les axes à modifier seraient principalement au nombre de cinq
- Les traits de fonctions et le système d’effets
- Les capabilities à la compilation
- La refonte de
Pin, du move et des emprunts de structures - Le
comptimeà la Zig - De petites améliorations de syntaxe et de bibliothèque standard
Traits de fonctions et système d’effets
- Rust dispose de traits pour les structures, mais la proposition est de pouvoir associer aux fonctions des traits/effects plus riches
- Les propriétés qu’une fonction peut avoir se résument ainsi
- Possibilité ou non de paniquer
- Taille de pile fixe ou non
- Exécution jusqu’au bout, ou utilisation de
yieldouawait - Type de continuation lorsqu’il s’agit d’une coroutine
- Fonction pure ou non
- Exécution indirecte de code
unsafeou non - Garantie de terminaison ou non
- Si les paramètres et le type de retour d’une fonction étaient exposés comme des associated types de la fonction, il deviendrait possible de référencer des types aujourd’hui inaccessibles en stable Rust
- L’exemple donné est un code qui nomme directement le type de retour d’une fonction, comme
some_iter::Output, pour le placer dans un champ de structure - Lorsqu’un bloc de code donné doit être garanti comme ne pouvant jamais panic, comme dans le kernel Linux, une annotation du type
#[disallow(Panic)]permettrait de vérifier la possibilité de panic, y compris dans les appels récursifs - Le compilateur traite déjà des traits liés aux fonctions comme
Fn,FnOnceetFnMut, mais la forme actuelle est jugée trop pauvre - Pour aller plus loin, un lien renvoie à l’article et à la présentation de Yoshua Wuyts sur les effect systems
Réduire les risques de chaîne d’approvisionnement avec des capabilities à la compilation
- La plupart des projets Rust importent de nombreuses crates tierces, et même une petite crate utilitaire peut créer un risque de chaîne d’approvisionnement via une mise à jour malveillante
- De la même façon que
unsafeest un opt-in explicite pour la sécurité mémoire, les fonctionnalités sensibles liées au système de fichiers, au réseau, à la FFI ou aux raw pointers devraient devoir être autorisées explicitement - La proposition consiste à ajouter des marker tags aux fonctions sensibles de la bibliothèque standard
- Par exemple,
std::fs::write(path, contents)écrit un fichier à un chemin arbitraire et aurait donc un tag du type#[cap(fs_write)] - Le compilateur taint automatiquement tout l’arbre d’appels qui invoque cette fonction
- Par exemple,
- Si une crate tierce a besoin de la capability
fs_write, l’appelant devrait l’autoriser explicitement dansCargo.tomlou via une annotation au point d’appel - Sans autorisation, le compilateur pourrait émettre une erreur indiquant que
foo::do_stuff()écrit sur le système de fichiers local, mais que la cratefoon’a pas été approuvée pour cette capability - Beaucoup de crates utilitaires comme
human-sizeouserden’ont besoin d’aucune capability particulière ; même si leur auteur ajoutait du code malveillant, l’écriture de fichiers ou les actions réseau pourraient être bloquées dès la compilation - Une autre approche serait de modifier les API sensibles pour qu’elles prennent un paramètre
Capabilityséparé- Par exemple,
std::fs::writeexigerait une valeurFsWriteCapability - La création des objets
Capabilityserait limitée à la root crate
- Par exemple,
- Cette approche ajoute du boilerplate mais offre plus de flexibilité, et il faudrait traiter de manière similaire les scripts
build.rset les blocsunsafe - Même si crates.io était compromis et qu’un code de cryptolocker était ajouté à
serde, l’approche par capabilities provoquerait une erreur de compilation avant que ce code ne s’exécute sur de nombreuses machines de développeurs ou ne soit inclus dans des binaires
Refonte de Pin, du move et de l’emprunt de champs de structures
Pinest vu comme un hack complexe destiné à contourner des lacunes du borrow checker, et comme un pansement né de la volonté de préserver la rétrocompatibilité- Ce qui serait réellement nécessaire ressemble plutôt à un marker trait
Moveindiquant les types déplaçables - Rust n’a actuellement pas de trait
Pin, maisUnpinet!Unpin, et cette double négation rend le concept plus difficile à comprendre Pinne s’appliquant qu’aux types référence, il entraîne l’apparition de code enveloppé dans desBoxun peu partout- Sont cités comme exemples des helper libraries comme les wrappers de streams Tokio,
ouroboros,async-traitetself_cell
- Sont cités comme exemples des helper libraries comme les wrappers de streams Tokio,
- Les fonctions qui reçoivent des valeurs pinned, comme
Future::poll(self: Pin<&mut Self>, ..), deviennent elles aussi complexes, et des crates séparées sont nécessaires pour gérer la projection - À l’intérieur d’une fonction, le borrow checker gère les variables dans des états « owned », « borrowed » ou « mutably borrowed », mais ces états ne sont pas directement visibles par le programmeur
- Une
async fnamène le compilateur à créer une structure cachée pour stocker les états de suspension, ce qui peut produire une situation où un champ en emprunte un autre - Rust n’a pas de syntaxe permettant d’exprimer qu’un champ de structure est dans un état borrowed, ni d’exprimer directement les lifetimes entre champs
- La direction proposée est d’étendre le borrow checker pour permettre d’écrire directement des emprunts de champs de structures
- Par exemple, une syntaxe d’« emprunt local » comme
y: &'Self::x Vec<usize> - Le compilateur saurait que
xest emprunté et appliquerait les mêmes contraintes qu’aux variables borrowed à l’intérieur d’une fonction
- Par exemple, une syntaxe d’« emprunt local » comme
- Une telle syntaxe pourrait aussi servir pour les structures self-referential ou pour manipuler, dans un AST, des formes comme
source: Stringetast_nodes: Vec<&'Self::source str> - Une structure ayant un champ borrowed ne pouvant pas être déplacée, elle n’implémenterait pas
Move, et l’on pourrait même envisager un traitMoverpour permettre des déplacements sûrs par soi-même
Le comptime à la Zig et le problème des macros Rust
- Le compilateur Rust semble en pratique gérer plusieurs langages à la fois : Rust, le langage de macros Rust et les proc macros
- Le point central de la critique est que Rust lui-même est bon, mais que le langage de macros ne l’est pas
- Le
comptimede Zig consiste à faire exécuter certaines parties du code au moment de la compilation par un petit interpréteur intégré au compilateur - Fonctions, paramètres,
ifet boucles peuvent être marqués comme code de compilation, tandis que le code non-comptime est émis dans le programme réel - L’
étude de cas std printde Zig montre un exemple où la format string est reçue comme paramètre comptime, puis analysée dans une bouclecomptimeafin de générer le code d’affichage - L’implémentation de
println!()en Rust appelle des fonctions internes commeformat_args_nl, que l’auteur suppose hardcodées dans le compilateur - La critique se poursuit en disant que même les auteurs du compilateur Rust semblent ne pas vouloir utiliser le langage de macros de Rust
Petites propositions de modification de la syntaxe et de la bibliothèque standard
- Un point évoqué est de corriger le fait que
Range<T>implémenteCopylorsqueT: Copy - Le problème de
deriveavec les associated types est également cité comme cible de correction- Issue liée : derive with associated types
- Exemple : Playground
- Une proposition est que les expressions
if letprennent en charge le AND logique- Forme souhaitée :
if let Some(x) = some_var && some_expr { } - Le contournement actuel consiste à faire un pattern matching sur le tuple
(some_var, check_foo()), mais même sisome_varvautNone,check_foo()est exécutée, ce qui diffère du comportement en short-circuit d’unifclassique - Exemple : Playground
- Forme souhaitée :
- L’ergonomie des raw pointers est aussi citée comme amélioration souhaitable
- Avec les références, on peut écrire
myref.x, alors qu’avec les pointers il faut écrire(*myptr).xou(*(*myptr).p).y - La position défendue est que le code
unsafedevrait être aussi facile que possible à lire et à écrire
- Avec les références, on peut écrire
- Une proposition est de modifier les types de collections intégrés afin qu’ils reçoivent explicitement un
Allocatordans leur constructeur, au lieu d’utiliser l’allocateur global asyncaurait également besoin d’améliorations, mais le sujet est laissé de côté car il demanderait un article séparé
Compatibilité et faisabilité
- La plupart des propositions ne sont pas compatibles avec Rust existant
- Même l’ajout de security capabilities introduirait une nouvelle forme de condition d’échec rompant la compatibilité semver des crates, ce qui nécessiterait une nouvelle Rust edition
- Il y a quelques années, l’auteur aurait peut-être rédigé une RFC, mais il dit vouloir éviter qu’une nouvelle idée non réalisée s’enlise dans un long fil de commentaires GitHub autour d’une RFC
- Il en revient donc finalement à l’idée de forker directement le compilateur, tout en restant confronté à la contrainte réaliste d’avoir trop de projets à mener
1 commentaires
Avis sur Hacker News
Contrairement à l’idée selon laquelle le processus de RFC de Rust serait un cimetière de bonnes idées, je pense que l’équipe cœur de Rust a raison de rendre difficile l’ajout de nouvelles fonctionnalités au langage
Il faut éviter que la surface du langage devienne boursouflée, incohérente et imprévisible. J’aimais aussi Swift au début, mais j’ai fini par abandonner à mesure que s’accumulaient des noms de fonctions redondants comme
isMultiple(of:), des règles d’analyse des accolades pour SwiftUI, des règles sur les types référence/valeur et la mutabilité, ou encore la notation abrégée des arguments de closures. Les bonnes idées sont courantes, donc j’aimerais que Rust reste aussi léger que possiblePar exemple, on peut écrire une fonction qui retourne
impl Trait, et une structure peut avoir des champs arbitraires, mais on ne peut pas mettre dans un champ de structure une valeur retournée sous forme deimpl Trait, parce qu’on ne peut pas nommer son type.if a && bfonctionne, tout commeif let Some(x) = x, mais pasif let Some(x) = x && b. J’aimerais que ce genre de choses soit corrigé. En nombre de lignes de code du compilateur, Rust grossirait, mais du point de vue de la complexité à apprendre et à utiliser, ces trous de fonctionnalités rendent au contraire le langage plus complexe.Pinn’a pas été un énorme travail à implémenter dans la bibliothèque standard, mais ce n’est absolument pas une fonctionnalité légère, et la charge cognitive est immense. Je préfère un borrow checker complexe et du code Rust facile à lire à un compilateur simple et un langage difficile à utiliserMême si 100 % des idées en attente étaient acceptées et finissaient par être traitées, cela donnerait cette impression à cause de l’écart entre le rythme des entrées et celui des sorties. Si vous voulez prendre de l’avance sur les auteurs de RFC, mieux vaut ne pas écrire de RFC, mais produire une implémentation prête pour la production, avec documentation et tests, pouvant être fusionnée proprement dans l’arbre
On ne peut les utiliser ni comme const generics d’autres types, ni comme tailles de tableaux. Si l’on a simplement besoin d’une valeur, il suffit de définir une fonction qui la retourne, donc en l’état ce n’est pas très utile. Si c’était correctement pris en charge, on pourrait se débarrasser de crates auxiliaires comme
generic_arrayettypenumdans les bibliothèques de cryptographie. Cela dit, je suis d’accord sur le fait que l’équipe Rust doit rester prudente dans l’ajout de fonctionnalitésLa situation des dépendances est assez grave, mais il semble que peu de gens veuillent l’admettre. Un exemple que j’ai vu récemment est le crate cargo-watch
À la base, c’est une application simple qui surveille les changements de fichiers et relance le compilateur, avec une implémentation de moins de 1 000 lignes. Mais si l’on vendore ses dépendances, on arrive à près de 4 millions de lignes de code Rust réparties sur plus de 8 000 fichiers. C’est excessif pour un simple surveillant de fichiers
https://crates.io/crates/cargo-watch
C/C++ est presque le seul cas, parmi les langages largement utilisés, où un gestionnaire de paquets à la npm n’est pas populaire ; la plupart des bibliothèques y sont donc autonomes, ou ont peu de dépendances, souvent optionnelles.
efswest un surveillant de système de fichiers en C++ de 7 000 lignes sans dépendance. Les bibliothèques single-header du monde de la programmation de jeux, commestb_*,cgltf, ainsi que Dear ImGui, ont été parmi les plus agréables que j’aie utilisées. Pour un nouveau gestionnaire de paquets, je pense qu’interdire les dépendances transitives pourrait être globalement bénéfique. Les grosses dépendances de bibliothèques peuvent être installées directement par l’utilisateur, remplacées par des callbacks, ou intégrées comme fonctionnalités standardhttps://github.com/SpartanJ/efsw
https://github.com/nothings/stb
https://github.com/jkuhlmann/cgltf
https://github.com/ocornut/imgui
Un surveillant de fichiers, surtout s’il doit prendre en charge plusieurs plateformes, n’est jamais vraiment simple, à mon avis
Une partie de
cargo watchimporte une bibliothèque wrapper de l’API Win32, qui est constituée de bindings générés automatiquement pour les appels Win32 et est donc forcément énorme. Les bibliothèques standard de la plupart des langages comptent aussi des millions de lignes, dont une application n’utilise qu’une partie. Boost en C++ est monstrueusement gros, mais les développeurs n’en utilisent que quelques extensions. Je préfère même un petit nombre de grosses dépendances maintenues et utilisées par beaucoup de monde à l’enfer façon npm où un paquet commeisOddouis evenpeut casser tout un écosystème ; Rust est globalement plus proche du premier casLa plupart des dépendances de
cargo-watchsemblent venir de trois exigences directes :clap,cargo_metadataetwatchexec.claptire beaucoup d’éléments CLI spécifiques aux plateformes, etcargo_metadatadoit forcément importer beaucoup de choses de l’écosystèmeserde.watchexecpourrait être amélioré : il dépend decommand-group, maintenu par la même organisation, qui exige inconditionnellement Tokio. Le problème plus large est qu’il est difficile de corriger facilement les dépendances excessives d’autres crates. Si un crate est bloqué sur une ancienne version dewatchexec, on peut parfois s’en sortir en ajustantCargo.lock, mais c’est généralement difficile et il faut contourner avec[patch]. Ce serait bien d’avoir un moyen simple de définir un « stand-in » pour remplacer une version précise d’un crate, mais cela ressemble à un gros sujet de recherche pour les gestionnaires de paquets existantsRust n’est plus un nouveau langage excitant ; il est dans une phase qui vise une adoption à grande échelle. Il est naturel et sain que le développement de fonctionnalités ralentisse
Aujourd’hui, les erreurs de conception sont bien plus dommageables qu’un rythme lent. Ce qui rend Rust intéressant, ce ne sont pas ses fonctionnalités cool, mais le fait qu’il représente une nouvelle catégorie de langage : sûr côté mémoire, sans garbage collection, et prêt pour la production. Le voir réellement adopté là où c’est important est plus intéressant que de rendre le langage encore meilleur, et cette adoption est facilitée par la confiance dans une gouvernance prudente
L’opérateur spread, les fonctions génératrices,
async, les fonctions fléchées,leftpad, le nouveauDate, etc. : la liste des fonctionnalités importantes est sans fin. JS est bien plus ancien et bien plus utilisé que Rust, avec plusieurs implémentations en production qui doivent toutes implémenter les nouvelles fonctionnalités de manière cohérente. Il y a eu une période de stagnation autour d’ES5, mais la différence semble être que le comité de standardisation ECMAScript s’est ressaisiLes Rustaceans sont tellement obsédés par l’idée de tout réécrire en Rust que, pour un article sur la réécriture de Rust, j’ai sincèrement cru que ce serait une blague méta-satirique
Il est un peu étrange de commencer par se plaindre de la lenteur des décisions, puis d’énumérer comme raisons de non-stabilisation des fonctionnalités qui n’ont pas grand-chose à voir avec la prise de décision
Par exemple, les coroutines sont bloquées parce qu’il existe des cas limites difficiles à résoudre correctement. Il n’y a pas, dans le compilateur, une implémentation terminée qu’il suffirait « d’activer » : c’est une implémentation inachevée qui fonctionne dans beaucoup de cas, mais qu’on ne peut pas activer dans la version stable. Pour les traits de fonctions aussi, il a été explicitement décidé de ne pas les stabiliser sous leur forme actuelle, pour plusieurs raisons techniques et à cause de leurs interactions avec de futures fonctionnalités. Si l’on revenait en arrière, certaines choses seraient conçues différemment, mais la plupart sont liées à des décisions prises aux débuts de Rust, quand l’équipe et les ressources étaient beaucoup plus réduites. On peut se dire qu’aujourd’hui de meilleurs choix seraient possibles, et envisager une rupture à la façon Rust 2.0, mais le désastre du passage de Python 2 à 3 a été si important que beaucoup préfèrent accepter les aspérités existantes. Quand on lit la newsletter hebdomadaire Rust, on voit que les validations de RFC et les décisions de stabilisation sont traitées chaque semaine. Il y a certes des cas qui prennent trop longtemps, mais les problèmes de personnes, de coordination et de temps sont souvent plus difficiles à résoudre que les problèmes techniques
Il faut aussi lire la réponse de Josh Triplett sur Reddit avec cet article. L’un des exemples centraux de l’article, Mutex, est tout simplement faux
https://old.reddit.com/r/rust/comments/1fpomvp/rewriting_rus...
Modification : le même commentaire se trouve aussi ici
https://news.ycombinator.com/item?id=41655268
La première chose que j’ai ressentie en apprenant Rust, c’est qu’il semblait déjà contenir presque toutes les fonctionnalités imaginables
Cela ne veut pas dire que l’équipe Rust n’a jamais refusé quoi que ce soit, mais les gens en veulent toujours davantage. Certaines demandes sont légitimes, mais d’autres donnent l’impression qu’il faudrait absolument ajouter au langage une fonctionnalité que seuls 2 % des développeurs utiliseront, et que seuls 1 % comprendront. Un langage complexe n’a pas besoin de devenir encore plus complexe. Zig est plus simple, probablement plus rapide, et connaît beaucoup moins de drama communautaire. J’aimerais que Zig reçoive davantage de financements
Rust ne cherche pas à intégrer toutes les fonctionnalités possibles. Cela dit, si l’on ne voit pas clairement quels problèmes résolvent des choses comme GAT ou TAIT, on peut avoir cette impression. Zig est peut-être un bon langage moderne, mais si l’objectif est la sécurité mémoire, ce n’est pas une option
Par exemple, l’interaction entre
Pinet les futures ajoute au langage une complexité absurde, dont une partie me semble inutile. J’aimerais qu’il existe un langage proche de Rust sansPindu tout. Je pense aussi qu’il y aurait moyen de simplifier le borrow checker, à la fois dans la syntaxe et dans l’implémentation, mais je n’y ai pas encore réfléchi concrètement. À ce stade, ce serait difficile à changer sans forker le langage, mais Rust ne sera pas le dernier langage à utiliser un borrow checker. J’espère que la prochaine génération pourra l’améliorer sans produire un langage encore plus grosJe vois bien du drama lié à Rust, mais il vient généralement de personnes qui résistent à son utilisation ou à son adoption. Comme la récente agitation autour de Rust for Linux. Je n’ai pas l’impression que ce soit courant au sein de la communauté elle-même, même si j’ai peut-être manqué quelque chose. Zig est excellent, mais il n’est pas encore prêt pour la production
L’auteur du commentaire lié a analysé en profondeur les primitives de synchronisation de plusieurs langages, puis a réécrit les primitives de synchronisation de Rust, comme
MutexetRwLock, afin qu’elles utilisent directement les primitives bas niveau de l’OS sur chaque grand système d’exploitationEn utilisant des mécanismes comme
futexsous Linux, il les a rendues plus rapides, plus petites et globalement meilleures, et au passage il a pratiquement écrit le livre sur la programmation parallèle en Rust. Il est aussi utile pour la programmation parallèle hors Rusthttps://www.oreilly.com/library/view/rust-atomics-and/978109...
Il n’a pas non plus passé 7 ans à simplement s’amuser du côté des coroutines. Il a ajouté les fonctions async, les traits pouvant contenir des fonctions async, ainsi que plusieurs fonctionnalités nécessaires à la standardisation de
AsyncWriteetAsyncRead, et il existe aussi une implémentation des générateurs en nightly. La question de savoir s’il faut accepter la complexité de coroutines totalement générales, ou s’arrêter aux générateurs, est encore débattue. Certaines fonctionnalités commeAsyncIteratoravancent lentement, mais beaucoup de travaux sont actifs. Il est toujours intéressant de voir une partie des gens dire que le langage est trop lent, tandis que l’autre se plaint qu’il va trop viteLes traits de fonctions et le système d’effets ont eux aussi fait l’objet récemment d’une importante exploration de conception, avec l’objectif de trouver une solution pour ne pas avoir à écrire plusieurs fois les fonctions selon chaque combinaison de
async,tryetconst. Le sandboxing des crates malveillantes n’est pas un problème de niveau langage ; il nécessite une combinaison de vérificateur et de sandbox d’exécution, et les composants WebAssembly semblent plus prometteurs. En revanche, il existe un fort intérêt pour les capabilities à la compilation, comme le choix de l’allocateur ou du runtime async, ou l’hypothèse d’une plateforme 64 bits ; le sandboxing des proc macros est souhaité non pas pour empêcher la malveillance, mais pour permettre une mise en cache correcteLes structures auto-référentielles ne sont pas un problème de syntaxe, mais un problème très difficile à traiter pour le vérificateur d’emprunts. Les emprunts partiels sont déjà pris en charge dans la capture des closures, mais l’enjeu est de maintenir une gestion de version sémantique stable lorsqu’on les expose dans une API publique. Des pistes comme des « borrow groups » nommés semblent prometteuses. Du côté de
comptime, plusieurs directions sont également explorées, et une RFC visant à renforcermacro_rulesa récemment été rédigée. L’impldeRangeest déjà en cours, associé à un changement incompatible via une édition. La combinaison deif letavec le AND logique existe comme fonctionnalité instable et est proche de la stabilisation. Plusieurs propositions ont aussi été faites pour améliorer la syntaxe d’accès aux champs via pointeur, mais la question reste ouverte de savoir si élargir encore la surface du langage est un gain ou une perte. Grâce aux éditions de Rust, beaucoup de choses peuvent être modifiées si la conception est suffisamment convaincante. Les 700 fonctionnalités instables sont un vrai problème : il faut un gros travail de nettoyage de celles qui ont peu de chances d’être stabiliséesDes macros plus déclaratives, la délégation de la logique des crates
-sysà des bibliothèques partagées, ainsi quecfg(version)/cfg(accessible)réduiraient fortement le besoin d’implémentations côté utilisateur, mais le runtime resterait toujours là. Plus j’y pense, plus les ACL de cackle me semblent être une bonne approche extensible pour suivre le travail des proc macros, des scripts de build et du code runtime, et pour auditer l’usage de l’arbre de dépendances. J’ai entendu dire quecargo-redpenévoluait aussi vers un outil d’audit des appels, mais j’imagine quelque chose de plus haut niveau, comme cacklehttps://github.com/cackle-rs/cackle
Je comprends que les gens se plaignent que Rust est un gros langage et ne veulent pas qu’il grossisse encore, mais conserver l’implémentation async à moitié cuite actuelle ne rendra pas le langage plus petit ni plus simple. Cela rend simplement le langage moins bon. Pour les emprunts partiels aussi, ce serait déjà bien que cela fonctionne au moins au sein d’une même crate, même si ce n’est pas dans une API publique. C’est un problème que l’on rencontre sans cesse en programmation réelle. Je me demande aussi pourquoi le sandboxing des crates malveillantes serait impossible au niveau du langage. Si une fonction n’a pas de code tiers
unsafedans son arbre d’appels et n’effectue pas d’appels système, elle ne peut manipuler que les arguments qui lui ont déjà été passés, les variables locales et les globales dans sa portée. Si l’on pouvait renforcer ce mur pour en faire une frontière de sécurité, cela semblerait réduire fortement les risques liés à la chaîne d’approvisionnement des dépendancesLa mission de Rust a toujours été difficile dès le départ : mêler performance, sécurité et expressivité. Avec le retrait de Mozilla, il a perdu son mode fondateur, et comme la plupart des membres de l’équipe centrale d’origine sont aussi partis, il n’est pas surprenant que les progrès aient ralenti
Personnellement, je trouve cela préférable à prendre une mauvaise direction
Si l’on réécrivait Rust, j’imagine qu’on irait plutôt vers moins de fonctionnalités que vers davantage
À la manière de QBE par rapport à LLVM, ce serait fournir 70 % de Rust avec 10 % du code. En retirant les macros et certaines fonctionnalités rarement utilisées, ce serait peut-être possible
https://c9x.me/compile/
Qu’il s’agisse de Rust ou d’autre chose, il est difficile de savoir à quel point c’est faisable avant de vraiment essayer