Zig: Pointer-Stabilität für ArrayLists

Zig: Pointer Stability for ArrayLists

Robbie Lyman stellt Pointer-Stability-Locks für std.ArrayList vor, die Speicherfehler verhindern, wenn Zeiger auf Elemente gespeichert werden. Die Locks, die bereits für Hash Maps existieren, werden mit lockPointers() und unlockPointers() aktiviert und lösen bei Verletzung der Stabilität einen Panik mit Stacktrace aus. Ein Beispiel zeigt einen subtilen Bug, der durch das Wachsen der ArrayList entsteht, und wie die Locks helfen, ihn zu finden. Zudem wird auf die Besonderheit hingewiesen, dass Operationen wie orderedRemove() die Zeigerstabilität beeinträchtigen können.

Das Problem ist, dass die Elemente von Context.lines.items von der _Position_ von Context.history.items abhängen, diese Position sich aber _ändern_ kann, wenn Context.history über seine aktuelle Kapazität hinaus wachsen muss.
  1. amluto

    Das wirkt schwach.

    In einer Sprache wie Rust "sperrt" der Compiler die Zeiger für dich, und du kannst es nicht vergessen.

    In einer Sprache wie C++ (und vermutlich Zig) könnte man zumindest theoretisch dafür sorgen, dass Iteratoren und Slices, die auf den Speicher eines dynamischen Arrays verweisen, eine Art Sperre halten, die den Speicher fixiert.

    Aber diese API verlangt, dass der Programmierer daran denkt, die Zeiger zu sperren, und auch, dass er die Sperre für den korrekten Codebereich am Leben hält. Und mir scheint, dass selbst im Beispiel im Blogbeitrag die Sperre völlig außerhalb der Funktion genommen wird, die Stabilität benötigt, sodass es nichts gibt, was die Sperre korrekt eingrenzt. Selbst das Typsystem kann nicht helfen – die problematische parse-Funktion kann nicht deklarieren, dass sie einen ArrayList-Parameter mit gesperrten Zeigern erwartet.

  2. _bohm

    Es ist eine nette Funktion, aber ich kann nicht anders, als zu denken: Wenn du einen stabilen Zeiger auf ein Element in einer Sammlung brauchst, ist ArrayList die falsche Datenstruktur? Vielleicht kann mir jemand ein Beispiel nennen, wann man das tun würde, anstatt z.B. einfach einen Index zu speichern. Alternativ könnte man eine Unrolled Linked List verwenden (früher SegmentedList in Zig, bevor sie in 0.16 entfernt wurde – ich bin mir nicht sicher, warum).

  3. portly

    Das ergibt viel Sinn, wenn man bedenkt, dass es mit dem Rest der Sprache konsistent ist. Es ist eine weitere Möglichkeit, Stolperdrähte in deinem Code zu legen, um eigene Programmierfehler zu erkennen. Ähnlich wie die Verwendung von Asserts in Funktionen, um Eingaben und Ausgaben zu prüfen.

    Ich nutze ArrayLists viel, daher freue ich mich darauf, das überall im Code einzusetzen, um sie zu härten.

    Ich kann mir vorstellen, dass das nicht jedermanns Sache ist, aber dann genießt man wahrscheinlich auch keine der anderen Explizitheiten.

  4. Rendello

    Nebenbei: Ein Zig-Syntax-Feature, das ich in Rust wirklich vermisst habe, wird im zweiten Codeblock gezeigt, nämlich präfixierte mehrzeilige String-Literale à la:

    const text =

    \\Dies ist ein langer Kommentar

    \\Aber ich kann ihn beliebig über Zeilen verteilen

    \\Und meine Einrückung beibehalten.

    ;

    Ich habe begonnen, die Rust-Makro-Bibliothek `docstr` [1] zu verwenden, die dasselbe macht:

    const TEXT: &'static str = docstr!(

    /// Jetzt kann ich das auch in Rust tun.

    /// Ich bevorzuge diesen Stil oft

    /// für lange Texte.

    );

    Es funktioniert sogar mit Makros (Beispiel aus der Doku):

    let greeting: String = docstr!(format!

    /// Hallo, mein Name ist {name}.

    /// Ich bin {} Jahre alt!

    age

    );

    1. https://docs.rs/docstr/latest/docstr/

  5. boricj

    Ich habe einen Moment gebraucht, um zu verstehen, dass dies auf Zeigeränderungen innerhalb des Containers prüft, und nicht die Datenstruktur wie eine SDL-Oberfläche sperrt/entsperrt.

    Ich habe kürzlich einen benutzerdefinierten C++-Container für einen Pfad implementiert, dessen Komponenten iterierbar waren, unterstützt durch einen std::string. Ich speichere einfach Indizes und eine Referenz auf den String, sodass meine Iteratoren nicht ungültig werden, wenn der std::string nach einer Modifikation neu allokiert wird. Viel weniger fehleranfällig für geringe Kosten.

Mehr von diesem Tag

2026-08-30