Hacker News new | ask | show | jobs
by pron 1610 days ago
As (safe) Zig eliminates overflows just as (safe) Rust does, and, like Rust, has not unsafe casts, I don't see how "all of the same classes of memory safety problems that are present in C or C++ are present in Zig." Also, Zig guarantees — just as Rust does — that all pointers and means of creating them are known, and precisely so, so analysis tools could work on Zig better than they do on C or C++. In C/C++, at best they could check pointers at the time of dereferencing, while for Zig they could do so at the time of deallocation. You once said that couldn't work because Zig would have dangling pointers lying around (presumably in addition to the one used for deallocation) because C does, but it might as well be the case that C programs leave dangling pointers because tools cannot detect them, which has affected the programming style, not the other way around.

It's OK to doubt claims/hopes about Zig, just as I doubt the claim that Rust can achieve more correct/secure programs than Zig for less effort. Without empirical data, there's really no way to know. So in the meantime, all there is to go on is personal appeal. But you cannot support your claim that Zig is "just like C" with the assumption that it is.

I think that, at the end of the day, there is what we might call an "ideological" difference between us. While we both accept that both language features and best practices — code reviews, tests, tools — reduce bugs, we draw the line of where it's worth to sacrifice one for the other in different places. It might also be the case that Rust's complexity doesn't sacrifice anything for you, but it does for me, as I'm uncomfortable with complex languages, and I think Rust easily makes the top four most complex "production" languages in history (together with C++, Ada, and Scala). So while even for me C is on the wrong side of my line (just as Idris is on the wrong side of yours), I reject extrapolating from C to Zig, because Zig makes many more guarantees at the language level, so without pertinent data about Zig, the question cannot be settled. If I thought Zig was "like C," I wouldn't have found it promising and so intriguing, either.

Having said all that, I don't want it to sound as if I'm willing to bet on Zig right now. I'm far too risk-averse for that. But I wouldn't bet on Rust right now, either. What it brings to the table doesn't offset, for me, its (still-)high risk. All I'm saying is that the revolutionary Zig hints at a promise of a new low-level language that could bring more to the table and justify the risk.

2 comments

P.S.

I guess we could summarise Rust's and Zig's core design hypotheses as follows: Even though both place the same emphasis on correctness, Rust doesn't compromise on memory safety (which, given what empirical data we do have, is an important component of correctness but certainly not equivalent to it), i.e. it adds all the sound language features needed to provide it, even at the cost of language complexity, while Zig doesn't compromise on language simplicity, i.e. it adds all the sound language features needed to provide memory safety up to the point they impact language complexity. I don't discount the possibility that there might be a language that could be safer than Zig yet less complex than Rust, or perhaps even as soundly-safe as Rust and as simple as Zig, but so far I haven't seen such a language.

Barring any empirical data, we cannot say which, if any, of those two approaches leads to better correctness (where by "better" I mean reaching the desired level of correctness needed for most low-level applications more cheaply), so we both lean on "ideology," where I prefer simplicity whereas you prefer sound guarantees — both of us in the name of correctness. I think we agree that both C and Idris are the wrong paths to correctness, but while we might reasonably disagree on the price we should pay for soundness, placing Zig's memory-safety in the same category as C's is just as exaggerated and misleading as placing Rust's soundness in the same category as Idris's.

By the way, I wouldn't at all be surprised if empirical research ends up finding no significant differences in correctness between the two, and, in fact, would guess it to be the most likely outcome given our inability to find significant bottom-line differences between "reasonable" same-generation languages so far.

> Even though both place the same emphasis on correctness

But they don't. At all. Rust treats correctness as paramount, not just memory safety (for instance, the existence of 6 different string types, or the PartialEq/Eq trait dichotomy are for correctness unrelated to memory safety). Zig doesn't.

Sure you can write correct programs in it, and that's what everybody wants, but the language doesn't make any efforts to make it easier than any others. Zig places as much emphasis on correctness as JavaScript[1], it's not C or C++ level of minefield, but when it comes to correctness the language won't help you.

Zig has cool (killer?) features like its seamless integration with existing C code, ease of cross compiling and a super cool metaprograming ability, there's no reason to oversell it on stuff it doesn't focus on: that's the best to disappoint people who'll try it.

In the same vein, talking about “safe Zig” vs safe Rust is misleading to the readers: all Zig is 100% unsafe by default, unless you compile it with ReleaseSafe or add a @setRuntimeSafety, and even if you opt-in to safety, the amount of safety is actually quite limited at the moment. There's a long time goal[2] to check for all kind of UB at runtime when safety checks are enabled, but it doesn't exist yet, and if you look at the afformentioned github issue, you'll see a bunch of “@andrewrk andrewrk removed this from the 0.x.0 milestone, added this to the 0.x+1.0 milestone”. At this point, the final vision of what “safe Zig” will look like isn't known yet! And unless Zig adopts a borrow checker or find an equivalent alternative (which would be super exciting, but is unlikely), it will incur costly runtime checks, making it undesirable in production as it will likely be slower than a regular managed language (it's not useless though, it will be like a better ASAN/UBSAN[3] that you can use during fuzzing, but pretty far from what Rust offers).

[1] and I say that as someone who spend a significant amount of time writing JavaScript for a living.

[2] https://github.com/ziglang/zig/issues/2301

[3] I say “better” because it would be strongly linked with the actual semantics of the languages (which can still change if the development of such tooling requires it) and not the retrofitted best-effort stuff you can have in C.

> Rust treats correctness as paramount, not just memory safety

My favorite example of this is that Mutex in Rust is a container type. I think it's very interesting to reflect on why other languages that could do this (basically everything but C and maybe Go) still don't do this. I think it has to do with how you can't stop pointers from escaping the critical section if you don't have a borrow checker.

> Rust treats correctness as paramount, not just memory safety... Zig doesn't.

I strongly disagree.

> but the language doesn't make any efforts to make it easier than any others.

Of course it does. Correctness is Zig's greatest emphasis, and so the language includes several important correctness features: explicitness (e.g. no overloads, no implicit calls), fast partial compilation, easy testing. Neither Rust nor Javascript have these important correctness features (Comparing Zig's explicit simplicity to JS is just as wrong as comparing its soundness to C. Zig is much sounder than C and much more explicit than JS. Zig is so revolutionary and different from everything else that we have little if anything to compare it to).

It's just that Zig's approach to correctness is different from Rust's. It relies less on soundness and more on simplicity. As someone who's been involved with formal methods for some years, I can tell you that even in that community, there are these two approaches, those who prefer soundness, and those who prefer ease, as two valid approaches to correctness. The soundness camp wants to eliminate certain bugs; the ease camp wants to find as many bugs as possible per unit of effort. I think all agree we need some combination of the two, but where the best sweetspot(s?) is (are?) is an open problem. We do know that there is a tradeoff between the two. To increase ease you must either remove soundness or offer soundness to eliminate a more restricted set of bugs. For example, sound static analysis covers fewer issues than full-blown "deep" sound proofs or model checking in exchange for greater ease, and concolic testing might uncover deep logic bugs, but at the expense of soundness.

> all Zig is 100% unsafe by default

That's incorrect.

> And unless Zig adopts a borrow checker or find an equivalent alternative...

You are defining the notion of correctness to coincide with what Rust does (i.e. sound memory safety). As Zig's correctness is handled completely differently (broad strokes: find less memory safety bugs, find more others), this makes your point tautological.

Both Rust and Zig have the same emphasis on correctness and the same dedication to features that support it — some help soundness, others ease — but their balance between the two, how they try to achieve correctness and by catching which bugs is different. We don't yet know which, if any, of these languages offer a better path to correctness. And if our assumption is that more soundness always equals more correctness, then neither of these languages are in the right direction, as both intentionally sacrifice a great deal of soundness — almost all of it, really — in the name of ease.

> Rust easily makes the top four most complex "production" languages in history (together with C++, Ada, and Scala)

Ada was "complicated" when it was released because it was being compared to C. Contrasting it against contemporary C++ or Rust, Ada 2012 is much simpler.