Zig: Pointer Stability for ArrayLists

(ziglang.org)

47 points | by tosh 4 hours ago

3 comments

  • amluto 1 hour ago
    This seems weak.

    In a language like Rust, the compiler will “lock” the pointers for you, and you can’t forget.

    In a language like C++ (and presumably Zig), one could, in theory at least, have the iterators and slices that reference the storage of a dynamic array hold some sort of lock that pins the storage.

    But this API requires the programmer to remember to lock the pointers and also requires the programmer to keep the lock alive for the correct region of code. And it looks to me like even the example in the blog post has the lock taken completely outside the function that requires stability, so there is nothing whatsoever that gets the lock scoping right. Even the type system can’t help — the offending parse function can’t declare that it wants a pointer-locked ArrayList parameter.

  • Rendello 21 minutes ago
    Aside: one Zig (syntax) feature that I really missed in Rust is shown in the second code block, namely prefixed multi-line string literals à la:

        const text =
            \\This is a long comment
            \\But I can split it among lines arbitrarily
            \\And keep my indentation.
        ;
    
    I've started using the Rust macro library `docstr` [1], which does the same thing:

        const TEXT: &'static str = docstr!(
            /// Now I can do it in Rust, too.
            /// I prefer this style a lot of the time
            /// for long texts.
        );
    
    It even works with macros (example from the docs):

        let greeting: String = docstr!(format!
            /// Hello, my name is {name}.
            /// I am {} years old!
            age
        );
    
    1. https://docs.rs/docstr/latest/docstr/
  • _bohm 2 hours ago
    It's a nice feature but I can't help feeling like, if you need a stable pointer to an item in a collection, ArrayList is the wrong data structure to use? Maybe someone can chime in and give me an example of when you'd do this instead of, e.g., just storing an index. Alternatively, you could use an Unrolled Linked List (FKA SegmentedList in Zig before it was removed in 0.16, not sure why).
    • hansvm 49 minutes ago
      I use it in a lot of places where I know the max capacity ahead of time -- ensureCapacity() followed by a lot of *AssumeCapacity()-styled commands. It's convenient for all of the ... convenience ... methods (append() requires some bookkeeping somewhere, appendSlice() requires more, and so on). In those usages, it's basically syntactic sugar over a slice. That's not a perfect solution, but it's reasonably good often enough that I keep doing it.

      The proposed change doesn't do much for me personally (memory safety is ensured in other ways, and if it weren't I wouldn't be annoyed debugging the allocator-observed errors), but I could see myself using it at some other point in time for the same class of usages, or I could see other people relying on it when they choose that class of coding.

    • dataflow 36 minutes ago
      I don't know Zig, but conceptually: a direct pointer is the fastest way to access an object. An arraylist is the fastest dynamic sequence of objects (fattest in access, not in growth). You use these when you need the performance. It's not often but it certainly happens. The most trivial example is a string that you append to but still need to pass to a C API in between that expects it to be contagious, but it's far more useful than just for storing characters.
      • _bohm 4 minutes ago
        Sure, in cases where you need elements to be contiguous in memory then certainly an unrolled linked list is not appropriate. There's usually not a meaningful performance difference between a pointer deref and an indexed array access, however.
    • bvrmn 58 minutes ago
      ArrayList is a very generic (pun not intended) structure and could be stretched quite freely in any direction with useful property of owning underlying slice. Like readonly preallocated ArrayList is a thing.
      • _bohm 49 minutes ago
        That's a good point. Using lockPointers would be a good way to enforce at runtime that your ArrayList is truly read-only.
    • nvme0n1p1 1 hour ago
      SegmentedList had a weird API, especially the way you control the list growth factor by the size of an inline array. And it hadn't kept up with stdlib norms in recent versions. I do hope it comes back eventually with an improved API.

      At least we got Deque in exchange. I use that far more often than I used SegmentedList.

      • _bohm 1 hour ago
        Aha, gotcha. To tell you the truth, I don't think I ever used it. I have a custom implementation I wrote because I didn't realize at the time that SegmentedList was an unrolled linked list :p
    • the__alchemist 1 hour ago
      My mental model of Zig is that it is explicitly the language for developers who prefer using pointers in business logic (instead of just in MMIMO, and are looking for something with improvements over C); i.e. exactly this class of abstraction.
      • _bohm 52 minutes ago
        Having written a bunch of Zig, I wouldn't say that the language design or culture explicitly encourages the use of pointers over indices in such situations. I would say it's more a language which trusts the programmer to make correct decisions about which constructs are appropriate in any given circumstance.
        • svachalek 23 minutes ago
          Hasn't the past 30 years of the Internet age taught us that given such trust, programmers will make the incorrect decision with horrifying predictability? The most trivial level of software security requires that pointer safety needs to be mathematically proven not up to human (or LLM) judgment.
    • beepbooptheory 1 hour ago
      Maybe one use case is if you are interfacing with external C library and you're stuck with pointers?