1 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Als ich vor etwa drei Jahren eine Bytecode-VM und einen Garbage Collector direkt in Zig und unsafe Rust geschrieben habe, war die menschenfreundliche Ergonomie von Zig im Vorteil, aber mit dem Beginn des Zeitalters der Coding-Agenten ist dieser Vorteil faktisch bedeutungslos geworden
  • Der Produktivitätsgewinn von 1,5- bis 5-fach für Entwicklerinnen und Entwickler durch die wichtigsten Features von Zig wird vom 100-fachen Produktivitätsgewinn durch Rust-basierte Coding-Agenten überrollt
  • Die Kernfunktionen von Zig wie die Allocator-Schnittstelle, Integer mit beliebiger Bitbreite, packed struct und comptime entfalten ihren Wert vor allem dann, wenn Menschen den Code direkt schreiben
  • Das Typsystem von Rust ist wirksamer dabei, Fehler von Agenten dank bounded polymorphism und der Erzwingung von Invarianten bereits zur Compile-Zeit zu verhindern
  • In einer Situation, in der die von Agenten erzeugte Code-Menge um das Hundertfache steigt, ist die Garantie auf Speichersicherheit von Rust ein entscheidender Vorteil gegenüber Zig

Zentrale Veränderungen

  • Zig hatte große Vorteile bei der Ergonomie von unsafe Code, aber da Menschen immer weniger Code direkt schreiben, sinkt auch der praktische Wert dieses Vorteils
  • Die 1,5- bis 5-fache Produktivitätssteigerung für menschliche Entwickler durch Zig-Features wird vom 100-fachen Gewinn durch Coding-Agenten in Rust überschattet
  • Ein großer Teil der bekanntesten Zig-Features verbessert die Bequemlichkeit beim händischen Schreiben von Code, doch für Coding-Agenten ist dieser Unterschied weit weniger wichtig
  • Vor etwa drei Jahren habe ich beim Schreiben einer Bytecode-VM und eines Garbage Collectors in Zig und unsafe Rust den Eindruck gewonnen, dass die Erfahrung beim Schreiben von unsafe Code auf der Zig-Seite besser ist
  • Auch nach dem Stand von 2026 ist Zig eine gute Sprache, aber Rust ist zur bevorzugteren Sprache geworden und passt auch besser zu Coding-Agenten

Zigs Allocator-Schnittstelle

  • Die Allocator-Schnittstelle von Zig macht es leicht, spezialisierte Allocators wie arena oder stack fallback einzusetzen, um bestimmte Codepfade zu optimieren
  • Wenn man eine Zeile Nutzereingabe liest, ist die Eingabelänge theoretisch unbegrenzt und erfordert daher einen Heap-Allocator, aber in der Praxis sind Eingaben meist kurze Suchbegriffe oder Pfade und damit deutlich kleiner als 1 KB
  • std.heap.stackFallback(256, heap_allocator) legt einen Puffer fester Größe auf dem Stack ab und wechselt nur dann auf den Heap, wenn die Eingabe darüber hinausgeht; im Normalfall kann also ohne Heap-Allokation gearbeitet werden
  • Früher gab es in Rust nichts, das der Allocator-Schnittstelle von Zig entsprach; wenn man also ein Vec<T> mit Custom Allocator brauchte, musste man die Implementierung der Standardbibliothek kopieren und anpassen
  • Der Collections-Source von Bumpalo war in dieser Form ein Fork der Standard-Collections, verbunden mit einem bump allocator
  • In Rust nightly gab es eine Zeit lang das Allocator-Trait, und inzwischen scheint es auf einem ausreichend guten Stand zu sein
  • Ein Unterschied ist, dass Rusts Allocator auf Traits basiert und daher statischen Dispatch nutzt, während Zigs Allocator auf einer vtable basiert
  • Eine communityweite Konvention, Datenstrukturen wie in Zig auf Basis von Allocator-Parametern zu entwerfen, gibt es in Rust nicht, aber da KI Code leicht kopieren und anpassen kann, ist diese Einschränkung weniger wichtig geworden

Integer mit beliebiger Bitbreite und packed struct

  • Zigs Integer mit beliebiger Bitbreite und packed struct machen Dinge wie CPU-Cache-Optimierung im Stil datenorientierten Designs, tagged pointer, NaN boxing und bitflags deutlich einfacher
  • Wenn man eine Obj-C-API zusammen mit Metal über die Objective-C runtime C API verwendet, kann id ein tagged pointer sein und nicht einfach ein ausgerichteter Heap-Objektzeiger
  • Übergibt man einem Code, der von Ausrichtung ausgeht, ein getaggtes NSNumber, kann UB entstehen; man muss also günstig prüfen können, ob es sich um einen Heap-Zeiger oder einen tagged immediate handelt
  • In einem vereinfachten Layout für Objective-C tagged pointer zeigt das niederwertigste Bit an, dass es sich nicht um einen Heap-Zeiger handelt, die nächsten 3 Bit identifizieren den Klassen-Slot, und die restlichen 60 Bit bilden die Payload
  • Zig kann mit enum(u3) und packed struct ein Bit-Layout wie class: TaggedClass, payload: u60 direkt als Typ ausdrücken
  • In Zig kann man mit @bitCast zwischen rohem u64 und ObjcTaggedPointer wechseln und in is_ns_number is_tagged sowie die Klasse .ns_number prüfen
  • Im entsprechenden Rust-Code liegen Konstanten wie TAG_MASK, CLASS_MASK, CLASS_SHIFT und PAYLOAD_SHIFT innerhalb von ObjcTaggedPointer(u64); bei der Erzeugung wird per OR verknüpft, beim Zugriff werden Masken angewendet
  • In Rust ist der Klassen-Slot keine echte Typinformation, sondern eine u64-Konstante, und diese handgeschriebene Vorgehensweise ist weniger ergonomisch als in Zig
  • In Rust ist es besser, Crates wie bitfield oder bitflags zu verwenden, aber beide hängen von proc macros ab und fühlen sich nicht so gut an wie Zigs packed struct
  • Mit Coding-Agenten fällt das Problem, dass solcher Code mühsam von Hand zu schreiben ist, deutlich weniger ins Gewicht

Der veränderte Wert von comptime

  • Zigs comptime ist das spektakulärste Feature, und abgesehen von einigen schwer zugänglichen Sprachen mit abhängigen Typen gibt es kaum eine Sprache, die Compile-Time-Evaluation so gut bietet wie Zig
  • In der Praxis vermisse ich comptime inzwischen nicht mehr oft; etwa 95 % der Nutzung entfielen darauf, generische Datenstrukturen mit parametrisierten Typen zu erzeugen
  • Ein typisches Muster ist fn ArrayList(comptime T: type) type, das einen Typ entgegennimmt und einen Struct-Typ mit items: []T, capacity: usize und allocator: Allocator zurückgibt
  • Das Typsystem von Rust ersetzt einen erheblichen Teil dieser Zig-artigen comptime-Generics und kann noch mehr Invarianten erzwingen
  • In den verbleibenden etwa 5 % der Fälle ist das Fehlen von comptime unangenehm, und eine verlässliche Alternative ist nur Codegen
  • Wenn man während der Spieleentwicklung hitbox geometry aus einem Tool hartkodiert in eine Datenstruktur übernehmen will, muss man in Rust Claude ein Skript schreiben lassen, das eine Rust-Datei erzeugt
  • Trotzdem braucht man Compile-Time-Evaluation in der Praxis nicht besonders oft

Die Vorteile von Rusts Typsystem

  • Das Typsystem von Rust wird als wertvollerer Tausch gegenüber Zigs comptime eingeschätzt und ist besonders stark im Bereich von Traits/Typeclasses für bounded polymorphism
  • In Zig denselben Grad an bounded polymorphism umzusetzen, ist sehr schwierig
  • Das Typsystem von Rust kann mehr Invarianten erzwingen und hilft dadurch, typische Fehler von Coding-Agenten zu verhindern
  • Im Spielecode wird das Crate euclid verwendet, um ein in der Grafikprogrammierung häufiges Problem zu vermeiden: die Verwechslung von Koordinatenräumen
  • Wenn man für jeden Koordinatenraum spezialisierte Typen wie Point<Screen> oder Point<World> erstellt, wird das versehentliche Vermischen von Welt- und Bildschirmkoordinaten bereits beim Kompilieren verhindert
  • Mit separaten Typen wie WorldPoint, WorldVector und ScreenPoint ist die Addition von point und vector innerhalb desselben Raums erlaubt
  • Über Translation2D::<f32, WorldSpace, ScreenSpace> kann man explizit vom Weltraum in den Bildschirmraum umwandeln
  • Umgekehrt ist Code wie let bad: ScreenPoint = player;, der WorldPoint direkt ScreenPoint zuweist, nicht erlaubt

Der Effekt, sich weniger mit Speicherproblemen befassen zu müssen

  • Wenn Coding-Agenten das Schreiben von 100-mal mehr Code ermöglichen, steigt auch die Menge an Speicherproblemen, die man in Zig-Code prüfen muss, um das Hundertfache
  • Ohne formale Verifikation wird die Oberfläche des Suchraums, den man zur Fehlersuche untersuchen muss, viel größer
  • In einer Situation wie heute, in der die erzeugte Code-Menge stark wächst, ist Rust attraktiver
  • Der klassische Trade-off bei Rust war, dass der borrow checker die Produktivität von Entwicklerinnen und Entwicklern senkt, wenn sie noch nicht damit vertraut sind; mit Coding-Agenten verliert dieser Nachteil aber stark an Bedeutung
  • Selbst wenn man in Rust unsafe verwendet, kann man Coding-Agenten Werkzeuge wie miri ausführen lassen, um zu prüfen, ob keine UB entsteht und ob die aliasing-Regeln von Rust eingehalten werden

Fazit

  • Zig ist weiterhin eine Sprache, die ich vermisse, und eine gute Sprache
  • In der Arbeitsweise des Jahres 2026 ist Rust die bevorzugtere Wahl und harmoniert auch besser mit Coding-Agenten

1 Kommentare

 
GN⁺ 3 시간 전
Lobste.rs-Meinungen
  • Mein früherer Teamleiter war ziemlich stark der Meinung, dass Copy-paste-Code nicht immer schlecht ist
    Wegen des DRY-Prinzips klang das instinktiv falsch oder zumindest kontrovers, aber er war ein sehr pragmatischer Mensch und wandte dieses Prinzip vor allem auf große Test-Codebasen an
    Seine Logik war, dass eine einfache, aber größere und redundantere Codebasis wartbarer sein kann, als krampfhaft eine clevere gemeinsame Schnittstelle zu bauen
    Seit ich LLMs nutze, komme ich auf denselben Gedanken zurück, und inzwischen wende ich ihn auch auf wichtigere Teile von Software an
    Codegenerierung ist schnell, und LLMs scheinen bei einfachen, aber redundanten Codebasen eher richtig zu liegen

    • Besonders bei Tests bin ich komplett auf der WET-Seite, also bei „write everything twice“
      Wenn man zu viel Abstraktion in Tests einbaut, nur um Redundanz zu reduzieren, werden sie schwerer verständlich und es besteht das Risiko, dass sie auf subtile Weise falsch sind
      Noch schlimmer ist, dass Tests, wenn sie die Abstraktionen des zu testenden Codes wiederverwenden, auf dieselbe Weise falsch sein können wie dieser Code
      Außerdem sind Tests im Gegensatz zu Anwendungscode praktisch kostenlos „komponierbar“
      Wenn man das Test-Harness nicht ernsthaft ruiniert hat, kann man Tests beliebig hinzufügen oder löschen, ohne andere Tests zu beeinflussen; da es keine Integrationsreibung gibt, entfällt damit auch ein Grund, Redundanz zu vermeiden
    • Stimme zu
      Bei Tests habe ich das schon als DAMP ausgedrückt gesehen: „Descriptive and Meaningful Phrases“, ein Prinzip, das Lesbarkeit über Einzigartigkeit stellt
      Dieses Prinzip kann Redundanz in Form ähnlicher wiederholter Codeabschnitte erzeugen, lässt Tests aber offensichtlicher korrekt erscheinen
      https://testing.googleblog.com/2019/12/…
      In der Go-Community gibt es ein ähnliches Sprichwort: „A little copying is better than a little dependency“ https://go-proverbs.github.io/
    • Als ich Repeat yourself, do more than one thing, and rewrite everything zum ersten Mal gelesen habe, hat mich das wirklich angesprochen
    • Der Teil, dass es instinktiv falsch klang, stimmt so eigentlich nicht; es war von Anfang an nie falsch
    • Ich habe viel daraus gelernt, Andrew Kelley beim Coden zuzusehen
      Ich bin dankbar, dass er Live-Coding geteilt hat
      Wenn ich mich richtig erinnere, hat er beim Starten von etwas oft den ähnlichsten existierenden Code gesucht, ihn komplett kopiert und dann von dort aus angepasst
      Ich dachte dann: „Du setzt dich nicht erst lange hin und überlegst dir, welche Abstraktion beide gemeinsam haben?“ – aber er hat einfach mit Copy-paste weitergemacht und war deutlich produktiver als ich
  • Interessant, wenn man das Timing von Buns Zig → Rust AI-Rewrite bedenkt https://xcancel.com/jarredsumner/status/2053063524826620129#m

    • Von den letzten 150 in Bun gemergten PRs betrafen 108 Speichersicherheit, und es ging um Dinge wie vergessene Aufräumarbeiten auf Fehlerpfaden, use-after-free, uninitialisierte Lesezugriffe, Out-of-bounds-Zugriffe und Reentranz
      Davon wären 75 in einer Sprache mit Destruktoren, Move-Semantik und Borrow Checker gar nicht erst kompilierbar gewesen
      Das heißt, ungefähr jeder dritte ausgelieferte PR ist vom Typ „auf dem Fehlerpfad Freigabe vergessen“
      Etwa 88 der 108 Fälle liegen in Zig, während die rund 14 auf der C++-Seite größtenteils Restkategorien wie Referenzzyklen und GC-Concurrency-Races sind, die in jeder Sprache übrig bleiben
      Der Unterschied zwischen Zig und Rust ist also real, und die Zig-Bugs sind genau von der Art, die sich mit Destruktoren und Ownership beheben lässt, während C++ bereits nahe am Boden angekommen ist
      Ohne stärkere Compile-Time-Garantien bleibt das ein Katz-und-Maus-Spiel
      Der Vorschlag ist, nicht immer wieder die größte Bug-Kategorie einzeln zu reparieren, sondern sie strukturell zu eliminieren
      bun/docs/rust-rewrite-plan.md at claude/phase-a-port · oven-sh/bun · GitHub
  • Bei der Stelle „in den restlichen 5 % der Fälle ist es ohne comptime unerquicklich, und die einzige Möglichkeit, zuverlässig zu gleichwertigen Ergebnissen zu kommen, ist Codegenerierung“ ist nicht klar, was der Autor meint
    Denn über prozedurale Makros sagt er nichts

    • Zigs comptime ist wirklich cool, aber Rusts Makro-System ist ebenfalls keineswegs zu unterschätzen
      Es ist zwar etwas umständlich, es sauber aufzubauen, aber man kann damit eine Menge machen
      Ich finde auch, dass Codegenerierung teilweise einen unnötig schlechten Ruf hat
      Mit build.rs-Skripten habe ich schon einige ziemlich lästige Probleme per Codegenerierung gelöst, und es funktioniert gut
      Vielleicht werde ich das später natürlich bereuen
  • Die Kernaussage des Artikels scheint ungefähr diese zu sein

    1. In einem speziellen Fall, bei dem Allokatoren je Datenstruktur angepasst werden, waren die Kosten von Code-Copy-paste ein schwieriges Problem
    2. Einige Features von Rusts Typsystem sind bequemer. Dass man den Agenten im gezeigten Beispiel aber nicht anweisen könnte, Rusts Typdesign nach Zig zu portieren und mit comptime die API-Form zu erzwingen, erscheint schwer glaubhaft
    3. Bei Features wie Bit-Flags oder SoA ist die Lesbarkeit des Codes nicht wichtig
    4. Wenn der Compiler die Abwesenheit von Speichersicherheitsfehlern garantiert, kann man „100-mal“ mehr Code reviewen
      Rust ist zwar eine gute Sprache, aber das ist trotzdem etwas überzogen
  • Wirkt wie Werbung für Coding-Agents

    • Es wirkt wie eine ehrliche Einschätzung dazu, wie Coding-Agents die Wahl der Programmiersprache beeinflussen
    • Oder vielleicht ist es Reverse-Psychology-Werbung für Zig …
  • Aus einem verlinkten Artikel desselben Autors: Ein sehr häufiger Fehler in der Grafikprogrammierung ist es, Koordinatenräume zu verwechseln, und das Typsystem ist stark genug, per Typen auszudrücken, welche Koordinatenräume und Transformationen gültig sind
    Dasselbe gilt für Währungen, Distanzen und Gewichte in SI- und imperialen Einheiten, validierte Strings versus benutzerbereitgestellte Strings und Geheimwerte
    Außerdem kann man mit gut gepflegten Zustandstypen unmögliche oder unsounde Zustände verhindern
    Aber persönlich ist das Feature, das ich mir in Rust am meisten wünsche, die vollständige Eliminierung von Data Races
    Data Races gibt es auch in verwalteten Sprachen
    Das „nimm doch einfach Go“ scheitert daran, dass in Go alles zwischen Threads referenzbasiert mutierbar ist, inklusive Slice-Akrobatik
    Sogar JavaScript, früher eine komplette Spielzeugsprache und doch eine der reinsten und sichersten Varianten, hat bei jedem await potenziell ein Race
    Ganz zu schweigen von der Bösartigkeit des everything-is-an-EventEmitter-Musters
    Also ja. Wenn es nur GC gäbe … 🤫

  • Es fühlt sich an, als würde der eigentliche Punkt etwas versteckt
    Coding-Agents können Python und JavaScript ebenfalls sehr gut
    Ob sie darin besser sind als in Rust, ist subjektives Herumwedeln, aber ich würde diese Sprachen trotzdem nicht für viele Aufgaben wählen
    Ich frage mich, ob das Problem darin liegt, dass sich Zigs Features oft ändern, oder ob die AI-Trainingsdaten einfach wegen der noch jüngeren Sprache unsauber sind

  • Für mich ist Zig schwerer zu schreiben als Rust, aber leichter zu lesen
    Im AI-Zeitalter liest man mehr Code, als man selbst schreibt, deshalb bevorzuge ich eher Zig

  • Angesichts der aktuellen Menge an generiertem Code ist die Aussage, Rust sei attraktiver, nur der erste Schritt
    Je mehr Computer Code schreiben, desto vorteilhafter werden formalere Sprachen sein
    Das wirkt wie eine weitere Phase der Debatte um dynamische Typisierung
    So nach dem Motto: „Dynamische Typisierung ist für Menschen einfacher, aber warum sollte man für die Maschine dieselbe Sache dreimal explizit angeben?“
    Typen, Lifetimes … was gibt es sonst noch, das für Maschinen leichter zu schreiben und zu konsumieren ist
    Ich frage mich, wie schwer es für Menschen werden wird, direkt in den Sprachen zu programmieren, in denen Computer künftig Code schreiben