News for hackers. Stuff that matters.
Re:Systems Programming with Zig (Book) by bjackman on Saturday October 04, 2025 @06:29AM (#45472294)
Not a comment on the book but I hope a slightly off topic thread is OK...

As a systems programmer, what is the selling point of Zig? For me, memory safety is the number 1, 2, 3, 4 and 5 problem with C. A new language that doesn't solve those problems seems very strange to me. And as a counterpoint, I will tolerate massive number of issues with Rust because it gives us memory safety.

I work in security so that may give me a bias. But maybe instead of "bias" it's more like awareness. If you don't deal with it day in, day out, it seems impossible to really internalise how completely fucking broken something like the Linux kernel is. Continuing to write e.g. driver code in an unsafe language is just not acceptable for our civilisation.

But, also maybe you don't need full complete memory safety, if a language has well designed abstractions maybe it can just be "safe enough in practice". I've worked on C++ code that feels that way. Is this the deal with Zig?

Re:Systems Programming with Zig (Book) by pron on Saturday October 04, 2025 @07:21AM (#45472508)
1. Lack of memory safety is a big problem particularly because it's a common cause of dangerous vulnerabilities, but spatial safety is more important than temporal safety for that reason, and Zig does offer spatial safety.

2. Other important issues of concern when it comes to security/correctness are language complexity and compilation speed. A complicated language with lots of implicitness can obscure bugs and make reviews slower. Slow compilation reduces the iteration speed and harms testing. Zig focuses on these two. The language is simple, with no implicitness - overloading of any kind is not allowed and neither are any kind of hidden calls. Even if security were the only concern, it's not clear at all how much complexity and compilation speed should be sacrificed for temporal safety.

3. Nevertheless, the language is still expressive. In any language, you want the algorithm to be clear and without extraneous or hidden details for that domain. I think Zig gets that about right for low-level programming (and C++ doesn't).

For me, the #1 problem with C is lack of safety and the #2 problem is lack of expressivity. With C++, the main problem for me is language complexity and implicitness, with slow compilation and lack of safety tied for number 2.

Re:Systems Programming with Zig (Book) by mi_lk on Saturday October 04, 2025 @01:07PM (#45474797)
Can you say more about spatial security v.s. temporal security and how Zig does them?
Re:Systems Programming with Zig (Book) by pron on Saturday October 04, 2025 @01:27PM (#45475017)
Spatial memory safety means that a language (at least in its subset that's designated "safe") doesn't allow you to manufacture pointers into memory that may contain data of a different type than what's expected by the pointer (we'll call such pointers "invalid"). The classic examples of spatial memory safety is guaranteeing that arrays are never accessed out of bounds (hence "spatial", as in pointers are safely constrained in the address space). Zig guarantees (except when using delineated "unsafe" code) such spatial safety.

Temporal memory safety is the guarantee that you never access pointers that have been valid at some point in time after they've become invalid due to reallocation of memory; we call such pointers "dangling" (hence "temporal", as in pointers are safely constrained in time). The classic example of this is use-after-free. Zig does not guarantee temporal safety, and you can accidentally have a dangling pointer (i.e. access a one-time valid pointer after it's become invalid).

Invalid pointers are especially dangerous because, in languages where they can occur, they've been a very common source of exploitable security vulnerabilities. However, violating spatial memory bounds is more dangerous, as the result is more easily exploited by attackers in the case of a vulnerability, as it's more predictable.

Re:Systems Programming with Zig (Book) by pRusya on Saturday October 04, 2025 @02:20PM (#45475437)
> So Zig is as expressive as C++. but not only safer, but also much simpler, and compiles faster

Tbh syntax-wise Zig feels more cryptic[1] at first than C++.

[1] e.g. `extern "user32" fn MessageBoxA(?win.HWND, [*:0]const u8, [*:0]const u8, u32) callconv(win.WINAPI) i32;` from https://ziglang.org/learn/samples/

Re:Systems Programming with Zig (Book) by pron on Saturday October 04, 2025 @02:39PM (#45475572)
I think that's just a matter of syntax habits, presumably because you're already familiar with C++ syntax. The syntax in your example is especially "cryptic" simply because it's an FFI signature (of a function that's not written in Zig and doesn't use the normal Zig data representations).
Re:Systems Programming with Zig (Book) by tredre3 on Saturday October 04, 2025 @04:28PM (#45476409)
To be clear the equivalent C++ code is:

    #include <windows.h>
    extern "C" __declspec(dllimport) int __stdcall MessageBoxA(HWND, LPCSTR, LPCSTR, UINT);
    // or
    extern "C" __declspec(dllimport) int __stdcall MessageBoxA(void *, const char[], const char[], unsigned int);
It's not exactly a stellar improvement.
Re:Systems Programming with Zig (Book) by npalli on Saturday October 04, 2025 @04:34PM (#45476461)
Lol, no this is the equivalent

  #include <windows.h>

 int main() {
      MessageBoxA(NULL, "world!", "Hello", 0);
      return 0;
  }
Re:Systems Programming with Zig (Book) by bjackman on Saturday October 04, 2025 @08:12PM (#45477877)
I guess this must vary by usecase, but in the Linux kernel work I do, expressiveness is just not an issue. C is a dumb language, it's fine. Coding is just such a small part of the engineering task. Schlepping to write nontrivial stuff in C can be a drag but it's just such a minor thing compared to the task of getting OS software designed correctly that I am not really excited about trying to improve on that axis.

Whereas the memory safety issue is totally fundamental and totally terrifying. There's no other way to solve it than with a new language. The cost of changing languages is staggering but we have no choice. Yet... Paying that cost and not eking out the maximum safety benefit that's practical... Seems a bit dodgy to me.

Re:Systems Programming with Zig (Book) by pron on Saturday October 04, 2025 @08:50PM (#45478026)
> There's no other way to solve it than with a new language.

That's not true, though. If memory safety is the only thing that you need and C doesn't have, there are products that offer memory-safety proofs for C (e.g. https://www.trust-in-soft.com). You do need to add some lifetime annotations and may want to change your code here and there, but it's overall much cheaper than a rewrite in a new language. I believe such solutions are more popular, too; few companies - even and especially those that care primarily about correctness - are crazy enough to justify a rewrite of an existing product just for memory safety.

> Paying that cost and not eking out the maximum safety benefit that's practical... Seems a bit dodgy to me.

The cost is not the same, though (and if you only consider rewrites, as I said, there are better options). It sounds like you're saying that any cost is worth any added safety. I don't think that's true, but if it were, then something like ATS is probably what you're after (it isn't, though, because it's practically nobody's choice). Anything short of that is some compromise between cost and safety. Rust's compromise is just different from Zig's. They're both significantly safer than C and a far way off from ATS. Everyone seems to agree that the sweet spot is somewhere on that spectrum - not C, not ATS - but there's no agreement on where.

Re:Systems Programming with Zig (Book) by kobebrookskC3 on Sunday October 05, 2025 @11:38AM (#45482362)
> there are products that offer memory-safety proofs for C

what does the c checked by this tool look like? for an example like https://play.rust-lang.org/?version=stable&mode=debug&editio... , does the tool accept the assignment with f and reject the assignment with g?

Re:Systems Programming with Zig (Book) by pron on Sunday October 05, 2025 @12:22PM (#45482822)
That particular code is shaped by Rust's specific scoping an lifetime rules, but generally - yes (provided you offer annotations for things the tool can't infer). Some changes to the code may be needed to make things easier, but it's still a lot cheaper than a rewrite in a different language.
Re:Systems Programming with Zig (Book) by kobebrookskC3 on Sunday October 05, 2025 @10:33PM (#45487114)
in this example, aren't the scoping rules for c pretty much the same?

what do the annotations for the tool look like? is the analysis local, in that it doesn't look into the bodies of other functions? if it is, surely you would have to have lifetimes and be generic over them.

how much c code satisfies the tool? if there's hardly any c satisfying the tool, there might actually be a larger ecosystem of rust code to use.

Re:Systems Programming with Zig (Book) by pron on Monday October 06, 2025 @12:30PM (#45493112)
So the annotations for such tools are pre/post conditions for certain properties (say, pointer validity, ownership). Like types, function annotations can sometimes be inferred, or they may need to be explicit. Once that information is known about a function, there's no need for the tool to look inside it again when analysing callers.

If you want to get a sense of what that may look like, see https://www.frama-c.com (which is a community project, so maybe not as smooth and polished like more professional tools).

Re:Systems Programming with Zig (Book) by KsassPeuk on Tuesday October 07, 2025 @03:09AM (#45500201)
Well, Frama-C is maintained thanks to public funding (Europe/France) and industrial users who use it for actual certification of systems. For example, Thales (Common Criteria EAL7), Airbus (DO-178C), EDF (ISO 60880). I don't know what you mean by professional tool if this is not professional ;)
Re:Systems Programming with Zig (Book) by hansvm on Saturday October 04, 2025 @09:18AM (#45473115)
You touched on the idea in your last paragraph. Comparing Zig to C using memory safety as a binary threshold instead of a continuous scale is apples to oranges.

For one, Rust isn't even at the far right of the memory safety scale, so any argument against Zig due to some epsilon of safety has to reckon with the epsilon Rust is missing as well. That scale exists, and in choosing a language we're choosing (among other things) where on that scale we want to be. Maybe Rust is "good enough", and maybe not.

For two, there's a lot more to software design than memory safety _even when your goals revolve around the safety and stability of the end product_.

So long as you get the basics right (like how Zig uses slices by default and has defer/errdefer statements), you won't have an RCE from a memory safety bug, or at least not one that you wouldn't likely see in Rust anyway (e.g., suppose you're writing a new data structure in both languages; suppose that it involves back-references or something else where the common pattern in Rust would involve creating backing memory and passing indices around, combined with an unsafe block on the actual cell access to satisfy the borrow checker; the fact that you had to do something in an unsafe block opens you up to safety issues like index wraparound causing OOB reads).

If memory safety is "good enough" from an RCE perspective, what are we buying with the rest of Rust's memory safety? Why is it so important? For everything else, you're just preventing normal application bugs -- leaking user data, incorrect computations, random crashes, etc. The importance of normal application bugs varies from project to project, but in the context of systems programming of any kind I'll argue that these are absolutely as critical as RCEs and the other "big" things you're trying to prevent via memory safety. The whole reason you try to prevent an RCE is to ensure that you have access to your files/data/tools and that an attacker doesn't, but if logic bugs corrupt data, crash your tools, and leak PII then you're still fucked. Memory safety didn't save you.

That shift in perspective is important. If our goal is preventing ordinary application bugs on top of memory-safety bugs it becomes blatantly obvious that you'd be willing to trade a little safety for some bug prevention.

Does Zig actually help reduce bugs though? IME, yes. The big thing it has going for it is that it's a small, cohesive language that gives you the power you need to do the things you're trying to do. Being small makes it possible to create features that compose well and are correct by default, and having the power to do what you're trying to do means you don't have to create convoluted (expensive, bug-prone) workarounds. Some examples:

1. `let` rebindings in Rust are a great feature, and I like them a lot. Better than 90% of the time I use them correctly. The other 10% I'm instead trying to create a new variable and accidentally shadowing an existing one. That's often not the end of the world, but if there exists any code after my `let` rebinding expecting the old value then it's totally incorrect (assuming compatible types so that you don't have a compilation failure). Zig yells loudly about the error.

2. The error-handling system in Zig is easy to get right because of largely compatible return types. By contrast, I see an awful lot of `unwrap` in Rust code in places that really shouldn't have it, and in codebases that try to use `?` or other more-likely-to-be-correct strategies the largely incompatible return types force most people into slapping `anyhow` around everything and calling it a day.

3. Going back to error-handling, Rust allows intentional discards of return values, but if you start by discarding the return value of a non-error function and the signature is later updated to potentially return errors Rust still lets you discard the value. Zig forces you to do something explicitly acknowledging that there was an error. 100% of the time the compiler has yelled at me for a potential logic bug it was correct to do so.

It really is just a bunch of little things, but those little things add up and make for a pleasant language with shockingly few bugs. Maybe my team is just even more exceptional than I already think or something and I'm over-indexing on the language, but in the last couple years I haven't seen a single memory safety issue, or much in the way of other bugs in the Zig part of the company.

Re:Systems Programming with Zig (Book) by tialaramex on Saturday October 04, 2025 @12:03PM (#45474296)
1. I guess by "let rebinding" you mean shadowing. Clippy has lints for shadowing which are default off, you might find that some patterns of shadowing which are legal in Rust are too often problematic in your software and so you should tell Clippy to warn you about them or even deny them by default.

For example you might decide to warn for clippy::shadow_unrelated which says if I have a variable named "goose" and then later in the function I just make another variable that's not obviously derived from the first variable but it is called "goose" anyway, warn me I did that.

2. I don't see any advantage of "largely compatible" error handling over anyhow, maybe you could be more explicit about what you want here or give an example.

3. What you're claiming for Zig here is a semantic check. This could exist but be fallible (it can't be infallible because Rice's Theorem), but I suspect the reality is that it's not a semantic check and you've misattributed your experience since I don't see any such check in Zig. Maybe an example would clarify.

Re:Systems Programming with Zig (Book) by hansvm on Saturday October 04, 2025 @04:07PM (#45476280)
I really didn't mean to make this about Zig vs Rust. That was a mistake. My real point is that Zig is also a great language, even when you care about safety.

That said, the things you asked about:

1. I suppose you might get away with linting all of shadow_reuse, shadow_same, and shadow_unrelated. Would that sufficiently disable the feature?

2. The biggest problems with overusing `anyhow` to circumvent the language's error system are performance and type safety. Perf is probably fine enough for most applications (though, we _are_ talking about systems programming, and this is yet another way in which allocation-free Rust is hard to actually do), but not being able to match on the returned error makes appropriately handling errors clunkier than it should be, leading to devs instead bubbling things up the call stack even more than normal or "handling" the error by ignoring any context and using some cudgel like killing connections regardless of what the underlying error was.

3. Rice's Theorem only tangentially related. Your goal, and one that Rust also strives for, is to make it so that changes to the type signature of a function require compatible changes at call sites, which you can often handle syntactically. Rust allows `let _ = foo()` to also silence errors, and where Zig normally allows `_ = foo()` to silence unhandled return values it additionally requires `try`, `catch`, or `return` (or similar control flow). You have options like `_ = foo() catch {}` or `_ = foo() catch @panic("WTF")` if you really do want to silence the error, but you'll never be caught off-guard by an error being added to a function's return type.

Re:Systems Programming with Zig (Book) by tialaramex on Sunday October 05, 2025 @08:20AM (#45480975)
Well, merely warning won't disable the feature. You could `forbid` these lints, and then require clippy for CI or something, the same way many people require unit tests pass in CI. I'd be very surprised if you find all three problematic. The problems you described feel like shadow_unrelated

You are absolutely right that anyhow doesn't do what you want for a no-alloc situation like a kernel driver or some ESP32 firmware - however it won't compile without an allocator, so, seems like you'd be shifting that problem hard left, the programmer who adds anyhow to their no allocator firmware can't build it.

Yes, I see now what Zig has here is literally a syntax check and so Rice doesn't apply because in Zig the function which returns an error is syntactically different and so Zig can say hey, this is an error function and those are different. I prefer the situation in Rust, but this is an important difference albeit one I don't agree with.

Re:Systems Programming with Zig (Book) by throwawaymaths on Saturday October 04, 2025 @11:54AM (#45474228)
the far right of the security scale is C (+ isabelle?) again (seL4)
Re:Systems Programming with Zig (Book) by unclad5968 on Saturday October 04, 2025 @07:24PM (#45477652)
> So long as you get the basics right (like how Zig uses slices by default and has defer/errdefer statements), you won't have an RCE from a memory safety bug

This the exact same argument C/C++ uses. Just don't do anything wrong, and nothing wrong will happen.

Re:Systems Programming with Zig (Book) by hansvm on Saturday October 04, 2025 @08:12PM (#45477879)
Not in the slightest. Here it's the language getting the basics right rather than the user.
Re:Systems Programming with Zig (Book) by ptrwis on Saturday October 04, 2025 @11:28AM (#45474021)
Maybe in the future, if the language has very good AI support, security guarantees of the language won't be as important, as it (ai) will find potential bugs well enough. This may be the case with Zig, as the language is simple and consistent, and the lack of macros will make it easier for LLMs to understand the code.
Re:Systems Programming with Zig (Book) by bigstrat2003 on Saturday October 04, 2025 @01:52PM (#45475195)
AI is not remotely capable enough to be trusted with that. Perhaps in the future it will be, but I'm not betting on it with the lack of improvement we have seen thus far.
Re:Systems Programming with Zig (Book) by throwawaymaths on Saturday October 04, 2025 @11:52AM (#45474213)
1. you get spatial memory safety which iirc is more common for security problems than temporal.

2. honestly for high performance applications you might reach for an ECS type system anyways (i think dom engines do this) at which point you would be getting around the borrow checker anyways.

Re:Systems Programming with Zig (Book) by bjackman on Saturday October 04, 2025 @07:59PM (#45477815)
> you get spatial memory safety which iirc is more common for security problems than temporal.

No, at least in the Linux kernel use-after-free is the biggest memory safety issue by a long way.

Maybe this isn't true in some other system software? Like if you are dealing with parsing. But, you don't really need memory safety for that usecase, fuzzing is unreasonably effective.

(Obviously for greenfield dev, safe languages are the way to go. But setting up a dank fuzzing pipeline is easier than migrating to a new language).