11 comments

[ 7.6 ms ] story [ 68.0 ms ] thread
I am the author of this post, let me know if you have any questions or feedback :)
I thought about dynamically sized types (DSTs) in zig recently. Was considering writing about it. I came to a different conclusion. Why not use zig's opaque?

It's pretty clean at this imo: Less metaprogramming but I think nicer to use in some cases.

  const Connection = opaque {
    pub const Header = struct {
      host_len: usize,
      // add more static fields or dynamic field lengths here
      //buff_len: usize,
    };

    pub fn init(a: std.mem.Allocator, args: struct { host: []const u8 }) *@This() {
      var this = a.allocWithOptions(u8, @sizeOf(Header) + host.len, @alignOf(Header), null);
      @memcpy(this[@sizeOf(Header)..], host);
      return this.ptr; 
    }

    pub fn host(self: *const @This()) []const u8 {
      const bytes: *u8 = @ptrCast(self);
      const header: *Header = @ptrCast(self);
      const data = bytes[@sizeOf(Header)..];
      const host = data[0..header.host_len];
      return host;
    }
  };
going off memory so I expect it to not actually compile, but I've definitely done something like this before.
> Zig does not, and will not, have VLAs in the language spec. Instead, you can allocate a slice on the heap. If you want to have the data on the stack, use an array as a bounded backing store, and work with a slice into it[.]

Too bad, aligned byte-typed VLAs (and a license to retype them as a struct) are what you need to get stack allocation across ABI boundaries the way Swift does it. (A long long time ago, SOM, IBM’s answer to Microsoft’s COM, did this in C with alloca instead of VLAs, but that’s the same thing.) I guess I’ll have to use something else instead.

> A long long time ago, SOM, IBM’s answer to Microsoft’s COM, did this in C with alloca instead of VLAs, but that’s the same thing.

Was kind of the other way around, given the whole OS/2 versus Windows history, and that COM started as the evolution of OLE and VBX technologies, Windows 9X and Windows NT weren't as COM heavy as OS/2 was with SOM.

There was no COM to worry about on Windows 3.x back in 1991.

https://www.edm2.com/index.php/SOM_%26_DSOM_-_An_Introductio...

Also SOM was so much better, bettwen C++, Smalltalk and C, with support for meta-classes and proper inheritance implementation across such disparate languages.

one thing I never understood about VLAs - discussion about them always hits a "can't put it on stack safely" and gets halted, forever

why not to make it heap-only type? it seems such a useful addition to type system, why ignore it due to one usecase?

(comment deleted)
Zig articles tend to get a little too excited about rediscovering longstanding techniques.

The author has described a metaprogramming utility for allocating a contiguous hunk of memory, carving this hunk into fields (in the article's example, a fixed-sized Client header, then some number of bytes for host, then some number of bytes for read_buffer, and then some for write_buffer). I'll acknowledge the syntax is convenient, but

1. we've done this since time immemorial in C. See https://learn.microsoft.com/en-us/windows/win32/api/evntcons...

2. you can implement this pattern ergonomically in C++, and even moreso once the C++26 reflection stuff comes online

3. the zig implementation is inefficient. It desugars to

  const Connection = struct {
    ptr: [*]u8,
    lens: struct {
      host: usize,
      read_buffer: usize,
      write_buffer: usize,
    }
  }
That first pointer is needless indirection and probably a cache miss. You should (unless you have specific performance data showing otherwise) store the sizes in the object header, not in an obese pointer to it. (It's bigger than even a fat pointer.)
You have raised a good discussion point or two, but I am not inclined to engage with them due to the tone you've created with the rest of your comment.

Would it be productive to jump into a thread on a Ruby article and puff your feathers about how you've always been able to do this in Perl, and also in Python 4 you can do XYZ? I don't think so.

For whatever reason, inevitably in threads on systems languages, someone comes in and postures themselves defensively like this. You might want to reflect on that.

I think you're misinterpreting OP's excitement here. The technique isn't novel but the ergonomics are.

> you can implement this pattern ergonomically in C++,

lmao

The author is literally proposing to implement arrays of variant types
Should I try Zig? I wanted to learn something that's more low level than the JVM/Node and i have been contemplating rust and go. zig never occurred to me until now.