New to Zig, and systems programming. What am I doing wrong here?

Dunno if you noticed, that tholmes’s reply used a different allocator (you were initially using std.heap.page_allocator, which is fine).

Zig init gives you a default allocator, and the best feature is that it can detect leaks (forgetting to free) and double frees (freed twice), which’ll be helpful for learning and building!

Indeed, details matters (in zig), even if not leak, but possible leak in edge case. Moving defer to top and list appends in bottom was a bit afterthought, focus being in alloc and mem copy. Great feedback as this also emphasizes my original point to learn properly mem allocations/frees, mem copies and list functions usage before trying to get into more complex things, with Io, and other tasks where one needs to catch data.

C-code I used over decade ago, didn’t have same ensure and assume methods, so even if I knew about those in zig, good reminder to be extra careful with any ad-hoc code written too - this was “new to zig…” topic after all, so good to cover details for learning purposes. Thanks.

Could you recommend some materials about that? I noticed that what stopped me go deeper is exactly the mem-related knowledge I missed: e.g., I don’t know why FixBufferAllocator needs a buffer at stack for a heap allocation, how std.process.Init.gpa holds on the things such that I don’t have to do deinit() manually, or in this thread, who owns the memory when there are several locations that store the data, stdout_buffer, stdin_buffer, instance of std.ArrayList, etc. I want to deepen my understanding of low-level stuffs and that’s why I’m also be doing “rewrite” C projects in Zig (for learning purpose). Any suggestion is appreciated!

If you have ZLS installed, you should be able to check a function’s doc comments to see its behavior. For example, most/all methods on Io.Reader have doc comments explaining their exact modifications to the reader’s state and/or whether they invalidate previous slices into the buffer.
Dedicated standard library documentation is currently a bit lacking, but for now a decent resource is the standard library tests, such as the tests for std.heap.FixedBufferAllocator. If you haven’t already seen it, the official Ziglang site also has a nice overview of learning resources.
Also, FixedBufferAllocator does not perform heap allocation; it acts as a bump allocator backed by the provided buffer, regardless of whether that buffer was itself originally allocated from stack, heap, or a raw memory region (on freestanding systems). When the buffer is full, it just throws an error.OutOfMemory and won’t try to allocate anything new.

I’ll try to help on the first one: FixedBufferAllocator is an allocator and is backed by a fixed buffer. So you need a buffer before you call .init(). One easy way to get such a buffer is to declare a buffer on the stack, which is useful because it’s super fast (as long as you can handle a comptime-known limit on the max size you’ll need before it starts returning .OutOfMemory errors and nothing needs to outlive this function call). But actually FBA doesn’t care where it is and you could also have gotten it from the heap via some other allocator instead.

I think what finally made it click is the deep realization that it’s all just bytes and everything that comes with that. But this is, I think, something that can’t easily be taught and is more such a thing that everyone needs to realize themselves. Just saying it over and over likely doesn’t help and for me it was understanding more of the underlying things(like computer architecture and operating systems) that finally did it.

I’ve heard good things about OSTEP and that it’s quite approachable for most people who’ve done programming before; though I’ve only taken a glance at it.

For those of a certain generation, we started with the bytes. Or at least, it wasn’t very long before we ran into them. There just weren’t many layers to dig through. Now, there’s so many ways to write code without ever needing to worry about how the computer actually works that people can go their whole careers without understanding how involved a simple program reading strings from the user really is.

It’s a real shame there aren’t any pre-virtual-memory machines around these days for people to learn on. Something with decent software on it, rather than just a microcontroller. There’s just no way to replicate the progression I had through 6502, Z80 and 68000 based machines that had all the tools you needed to make anything you wanted (paint programs, compilers, debuggers) and thriving communities around them.

That’s a deep pull, very good