Live data from Hacker News

I'm too dumb for Zig's new IO interface

openmymind.net

251–260 of 329 posts

Re: I'm too dumb for Zig's new IO interface

#251
post #226

Earlier quoted context omitted.

The key problem with Zig nowadays is how much of its community and adoption is driven by anti-Rust sentiment. As a result, while Rust puts beginner onboarding and documentation at the center of its culture, as opposed to the “C neckbeard”'s culture, Zig is going the other way around. (Loris Cro being a key community figure isn't helping in any way, and it's a good remainder that if you don't clear up your community f…

> anti-Rust sentiment I have not seen much of any anti-Rust sentiment in the community. There's a lot of people in the community who do Rust, like rust, and work on rust projects. If the Zig community has an anti-anything sentiment, it's against C++.

Tell that to the “VP of community @ zig software foundation” who spend a significant fraction of its time insulting Rust maintainers or mere users here or on Twitter when he's not busy writing anti-Rust rants on his blog.

(You won't have to seek long in his HN history to find an instance of such behavior)

Re: I'm too dumb for Zig's new IO interface

#252

Earlier quoted context omitted.

Breaking changes and limited support is one thing. But docs is not something you add at at the end. Lacking docs and Lacking tests is code that should not be released. They target embedded c market. Limited support and features is expected and common. But being lax with docs and testing is not acceptable.

> But being lax with docs and testing is not acceptable. First: Those two things are not the same. The Zig stdlib and compiler have tons of tests, and it's actually quite rare to stumble over implementation bugs. Second: I'd rather take no docs at all than per-function or per-type doc-headers which just repeat what the code does in natural language (and then may quickly get stale). Some parent wrote about generating…

I fundamentally disagree on code readability. A comment on the function header can save an hour of code reverse engineering for a complex library. Reading code is a last resort, and stale code docs is itself a red flag.

This post itself is a great example. The code is not self explanatory unless you understand the library below. So if this was library code; you would have to reverse through two abstraktion walls to understand it.

And as for high level documentation... it would be very helpful to have that DURING the development process to discuss the implementation, and quickly get people up-to-speed in the plan during code review. Id write that before the code.

I mention tests, because they are 10x more work to write than docs. So if the developer skips out on docs, my trust in their testing is also destroyed.

Re: I'm too dumb for Zig's new IO interface

#253

Earlier quoted context omitted.

Why is he wrong? Here's an excerpt from the close(2) syscall description: RETURN VALUE close() returns zero on success. On error, -1 is returned, and errno is set to indicate the error. ERRORS EBADF fd isn't a valid open file descriptor. EINTR The close() call was interrupted by a signal; see signal(7). EIO An I/O error occurred. ENOSPC EDQUOT On NFS, these errors are not normally reported against the first write whi…

It's unfortunate that the original authors of this interface didn't understand how important infallibility is to resource deallocation, and it's unfortunate that NFS authors didn't think carefully about this at all, but if you follow the advice of the text you pasted and read the section about how you can't retry close() after an error, it is clear that close is, in fact, a fundamentally infallible operation.

If the flush (syscall) fails, it's not possible to recover in user space, therefore the only sensible option is to abort() immediately. It's not even safe to perror("Mayday, mayday, flush() failed"), you must simply abort().

And, the moment you start flushing correctly: if(flush(...)) { abort(); }, it becomes infallible from the program's point of view, and can be safely invoked in destructors.

File closure operations, on the other hand, do have legitimate reasons to fail. In one of my previous adventures, we were asking the operator to put the archival tape back, and then re-issuing the close() syscall, with the driver checking that the tape is inserted and passing the control to the mechanical arm for further positioning of the tape, all of that in the drivers running in the kernel space. The program actually had to retry close() syscalls, and kept asking the operator to handle the tape (there were multiple scenarios for the operator how to proceed).

Re: I'm too dumb for Zig's new IO interface

#254
post #236
post #138

Earlier quoted context omitted.

> we already have enough unsafe languages as it is By that logic, we definitely have enough safe languages as it is, as there are many more. But this safe/unsafe dichotomy is silly, and is coloured by languages that are unsafe in some particular ways. 1. Memory safety is important because memory-safety violations are a common cause of dangerous security vulnerabilities. But once you remove out-of-bounds access, as Zi…

> Given Zig's safety guarantees (which are stronger than C++'s), and given its goals (which are different from C++'s), the question should be what should we be willing to give up to gain safety from use-after-free given the language's goals. Would more safety be better if it cost nothing? The problem with this statement is that without a memory safety invariant your code doesn't compose. Some code might assume no UAF…

> The problem with this statement is that without a memory safety invariant your code doesn't compose

Yes, but that holds for any correctness property, not just the 0.0001% of them that memory safe languages guarantee. That's why we have bugs. The reason memory safety is a focus is because out-of-bounds access is the leading cause of dangerous vulnerabilities.

> The goal of all engineering disciplines, including software, should be a minimization of errors and defects.

Yes, but practical minimisation, not hypothetical minimisation, i.e. how can I get the least bugs while keeping all my constraints, including budget. Like I said, a language like Rust exists because minimisation of errors is not the only constraints, because if it were, there are already far more popular languages that do just as much.

> You'll notice a stark contrast between Engineering and Computer Tech.

I'm not sure I buy this, because physical, engineered objects break just as much as software does, certainly when weighted by the impact of the failure. As to learning our lessons, I think we do when they are actually real. Software is a large and competitive economic activity, and where's there's a real secret to more valuable software, it spreads like wildfire. For example, high-level programming languages spread like wildfire; unit tests and code review did, too. And when it comes to static and dynamic typing, the studies on the matter were inconclusive except in certain cases such as JS vs TS; and guess what? TS has spread very quickly.

The selective pressures are high enough, and we see how well they work frequently enough that we can actually say that if some idea doesn't spread quickly, then it's likely that its impact isn't as high as its fans may claim.

> And how do programmers react to it? With disgust and a desire for less safety.

I don't think so. In such a large and competitive economic activity, the assumption that the most likely explanation to something is that the majority of practitioners are irrational seems strange to me. Rust has had some measure of adoption and the likeliest explanation for why it doesn't have more is the usual one for any product: it costs too much and delivers too little.

Let's say that the value, within memory safety, between spatial and temporal safety is split 70-30; you know what? let's say 60-40. If I can get 60% of Rust's value for 10% of Rust's cost, that a very rational thing to do. I may even be able to translate my savings into an investment in correctness that is more valuable than use-after-free.

Re: I'm too dumb for Zig's new IO interface

#255
post #237
post #198

Earlier quoted context omitted.

It isn't optional, and yet it's also not at any cost, or we'd all be programming in ATS/Idris. From those languages' vantage point, the guarantees Rust makes are almost indistinguishable from C. Yet no one says, "the conversation is over unless we all program in languages that can actually guarantee correctness" (rather than one that guarantees the lack of the eighth most dangerous software weakness). Why? Because it…

> It isn't optional, and yet it's also not at any cost, or we'd all be programming in ATS/Idris. In a better, saner world, we'd writing Ada++ not C++. However, we don't live in a perfect world. > The goal isn't to write the most correct program; it's to write the most correct program under the project's budget and time constraints. The goal of ANY software engineer worth their salt should be minimizing errors and def…

> In a better, saner world, we'd writing Ada++ not C++.

As someone who worked on safety-critical air-traffic-control software in the nineties, I can tell you that our reasons for shifting to C++ were completely sane. Ada had some correctness advantages compared to C++, but also disadvantages. It had drastically slower build times, which meant we couldn't test the software as frequently, and the language was very complicated that we had to spend more time digging into the minutiae of the language and less time thinking about the algorithm (C++ was simpler back then than it is now). When Java became good enough, we switched to Java.

Build times and language complexity are important for correctness, and because of them, we were able to get better correctness with C++ than with Ada. I'm not saying this is universal and always the case, but the point is that correctness is impacted by many factors, and different projects may find achieving higher correctness in different ways. Trading off fewer use-after-free for longer build times and a more complex language may be a good tradeoff for the correctness of some projects, and a bad tradeoff for others.

> If GC is acceptable or you

BTW, a tracing GC - whose costs are now virtually entirely limited to a higher RAM footprint - is acceptable much more frequently than you may think. Sometimes, without being aware, languages like C, C++, Rust, or Zig may sacrifice CPU to reduce footprint, even when this tradeoff doesn't make sense. I would strongly recommend watching this talk (from the 2025 International Symposium on Memory Management), and the following Q&A about the CPU/footprint tradeoff in memory management: https://www.youtube.com/watch?v=mLNFVNXbw7I

Re: I'm too dumb for Zig's new IO interface

#256

Earlier quoted context omitted.

It's unfortunate that the original authors of this interface didn't understand how important infallibility is to resource deallocation, and it's unfortunate that NFS authors didn't think carefully about this at all, but if you follow the advice of the text you pasted and read the section about how you can't retry close() after an error, it is clear that close is, in fact, a fundamentally infallible operation.

If the flush (syscall) fails, it's not possible to recover in user space, therefore the only sensible option is to abort() immediately. It's not even safe to perror("Mayday, mayday, flush() failed"), you must simply abort(). And, the moment you start flushing correctly: if(flush(...)) { abort(); }, it becomes infallible from the program's point of view, and can be safely invoked in destructors. File closure operation…

> In one of my previous adventures, we were asking the operator to put the archival tape back, and then re-issuing the close() syscall, with the driver checking that the tape is inserted and passing the control to the mechanical arm for further positioning of the tape, all of that in the drivers running in the kernel space.

Why can't the OS itself do the prompting in this case, as part of processing the original close()? MS-DOG had its (A)bort/(R)etry/(I)gnore prompt for failing I/O operations, and AmigaOS could track media labels and ask the user to "insert $MEDIA_LABEL in drive".

Re: I'm too dumb for Zig's new IO interface

#257

Earlier quoted context omitted.

Why is he wrong? Here's an excerpt from the close(2) syscall description: RETURN VALUE close() returns zero on success. On error, -1 is returned, and errno is set to indicate the error. ERRORS EBADF fd isn't a valid open file descriptor. EINTR The close() call was interrupted by a signal; see signal(7). EIO An I/O error occurred. ENOSPC EDQUOT On NFS, these errors are not normally reported against the first write whi…

It's unfortunate that the original authors of this interface didn't understand how important infallibility is to resource deallocation, and it's unfortunate that NFS authors didn't think carefully about this at all, but if you follow the advice of the text you pasted and read the section about how you can't retry close() after an error, it is clear that close is, in fact, a fundamentally infallible operation.

Yeah, close() can't fail, but it can return an error. It's kind of odd.

How could one fix that though? It seems pretty unavoidable to me because write() is more or less asynchronous to actual disk I/O.

You could add finalize() which is distinct from close(), but IMHO that's even more confusing.

Re: I'm too dumb for Zig's new IO interface

#258

Earlier quoted context omitted.

It's unfortunate that the original authors of this interface didn't understand how important infallibility is to resource deallocation, and it's unfortunate that NFS authors didn't think carefully about this at all, but if you follow the advice of the text you pasted and read the section about how you can't retry close() after an error, it is clear that close is, in fact, a fundamentally infallible operation.

If the flush (syscall) fails, it's not possible to recover in user space, therefore the only sensible option is to abort() immediately. It's not even safe to perror("Mayday, mayday, flush() failed"), you must simply abort(). And, the moment you start flushing correctly: if(flush(...)) { abort(); }, it becomes infallible from the program's point of view, and can be safely invoked in destructors. File closure operation…

If the tape drive failed close() in a way that did not deallocate the file descriptor, that was just straight up a bug.

Retrying close() is dangerous, if the file descriptor was successfully deallocated, it might have already been re-allocated by another thread. I'd guess the program you're describing was single threaded though (it can still bite there though)

Re: I'm too dumb for Zig's new IO interface

#259

Earlier quoted context omitted.

If the flush (syscall) fails, it's not possible to recover in user space, therefore the only sensible option is to abort() immediately. It's not even safe to perror("Mayday, mayday, flush() failed"), you must simply abort(). And, the moment you start flushing correctly: if(flush(...)) { abort(); }, it becomes infallible from the program's point of view, and can be safely invoked in destructors. File closure operation…

> In one of my previous adventures, we were asking the operator to put the archival tape back, and then re-issuing the close() syscall, with the driver checking that the tape is inserted and passing the control to the mechanical arm for further positioning of the tape, all of that in the drivers running in the kernel space. Why can't the OS itself do the prompting in this case, as part of processing the original clos…

Because DOS relied on BIOS interrupt 10h to handle I/O:

  mov si, GREETINGS_STRING
  print_loop:
    lodsb                  ; Load next byte into AL, advance SI
    cmp al, 0              ; Check for null terminator
    je done

    mov ah, 0Eh            ; BIOS teletype output
    mov bh, 0              ; Page number = 0
    mov bl, 07h            ; Light gray on black in text mode
    int 10h                ; Print character in AL

    jmp print_loop
  done:
    ...

  GREETINGS_STRING db "Hello, BIOS world!", 0
And linux doesn't rely on BIOS for output I/O, it provides TTY subsystem and then programs use devices like /dev/tty for I/O. Run $ lspci in your console: which of those devices should the kernel use for output? The kernel wouldn't know that and BIOS is no longer of any help.

Re: I'm too dumb for Zig's new IO interface

#260
post #105

Earlier quoted context omitted.

This really struck a chord with me. Writing documentation is an act of explaining something to others. Explaining something to others is a great way to test your own understanding. If it's hard to explain to someone else, then maybe it's the wrong design. If you don't through that exercise, you're much more likely to build confusing, difficult-to-use APIs.

> you're much more likely to build confusing, difficult-to-use APIs. have not found this to be the case with zig in general. you could easily make the opposite argument, that documenting things (especially quirks) can give you license to build confusing APIs.

> > you're much more likely to build confusing, difficult-to-use APIs.

> have not found this to be the case with zig in general.

Dunno how general it is, but TFA we're discussing here contains an example, so AFAICS it seems at least a little too common.

Post reply on HN