Earlier quoted context omitted.
it's really hard to say which of them leads to the fewest memory leaks Not the one with manual memory management.
I would say: not the ones with spooky implicit actions and hidden heap allocations, but we won't know until we actually have data. When writing in a low-level language, I always want to know where I'm allocating and where I'm deallocating. Zig makes allocations easier to spot than in C/C++/Rust, and deallocations easier to spot than in C++/Rust. That's just how I like it. I'm not saying everyone must have the same pr…
Partially Matching Zig Enums
131–140 of 170 posts
Re: Partially Matching Zig Enums
#132Earlier quoted context omitted.
And if you divide by 0 your program still crashes. The reality is that you can use vectors for memory allocations and you never have to worry about it. If you do need to wrap resource allocation, you do it once, test it and it will probably work from then on. This is much better than the alternative of having to remember to free the memory, close the file, unlock the mutex correctly every single time you need one.
I don't agree that "you never have to worry about it" unless you're also using smart pointers, which is rarely what I want. I think that the alternative where all allocations and deallocations are made clear (in Zig, allocating routines are "coloured" by convention) is the better alternative, at least for the kind of low-level programming I do and for my way of thinking about low-level code. When I write code where I…
If you don't want memory leaks, it probably is what you want.
There isn't a ton of difference between putting a delete in a destructor and using a smart pointer, but the best approach is to go beyond smart pointers and just use a vector, which does everything for you.
A lot of this seems like you haven't done a lot of modern C++ to see how elegant and smooth it is.
Re: Partially Matching Zig Enums
#133Earlier quoted context omitted.
I think it is relevant exactly because Rust exceptionalism is based on sloppy arguments that are fallacious because they narrow down topics and definitions in some invalid way, i.e. only considered memory safety while ignoring safety in general, only considering a specific definition of memory safety, only considering the safe subset of Rust, only accepting language-level safety, etc. until at the end it looks that R…
I completely agree, but given that even in Java, which eliminates the memory leaks Rust doesn't, programs still have bugs and security vulnerabilities, I don't think it's about what is and isn't memory safety. Most of the software that runs the world has been written in memory-safe languages for a very long time. It's more about understanding the significance and role of memory safety. With that comes the insight tha…
Re: Partially Matching Zig Enums
#134This post shows how versatile Zig's comptime is not only in terms of expressing what to pre-compute before the program ever runs, but also for doing arbitrary compile time bug-checks like these. At least to me, the former is a really obvious use-case and I have no problem using that to my advantage like that. But I often seem to overlook the latter, even though it could prove really valuable.
I love the idea, but something being "provable" in this way feels like relying on optimisations. If a dead code elimination pass didn't remove the 'comptime unreachable' statement, you'll now fail to compile (I expect?)
Unlike dead code elimination (which is an optimization pass that removes provably unused code), Zig's compile-time evaluation is deterministic and mandatory. When you use `inline .a, .b`, the compiler must generate separate instantiations for each enum variant. Within each instantiation, `ab` becomes a comptime-known value, making the inner switch deterministic at compile time.
This is similar to how C++ template instantiation works: the compiler doesn't "optimize away" unused template specializations—it simply never instantiates them in the first place. Zig's `comptime unreachable` works the same way: unreachable branches are never even considered for compilation because the control flow is resolved at compile time.
The difference matters because optimization-based approaches can vary between compiler versions or optimization levels, while Zig's semantic guarantees remain consistent. This makes `comptime unreachable` suitable for expressing static invariants that must hold regardless of compilation flags.
Re: Partially Matching Zig Enums
#135Earlier quoted context omitted.
> Maybe? You forgot /s there? Neither are memory safe, so if you're going by the "safe in practice" definition then it has to be verified experimentally. Hence - maybe. > Can you show RCE using this? RCE and Undefined Behavior are two intersecting sets. Not all UB is RCE, but what all UBs are hard to track bugs that happen at most inconvenient times. > You can’t prove Rust code "safe" in the absolute. Sure you can't…
> Neither are memory safe, so if you're going by the "safe in practice" definition then it has to be verified experimentally. Hence - maybe. This is a ridiculous claim that it’s only "maybe". It’s so obvious, it’s like saying cars are not safe to drive, but if you use seatbelts and have airbags, then MAYBE they’re safer. I have verified this experimentally, like millions of other people. This argument is totally in b…
Given enough effort, you can banish all UB and their related CVEs from a codebase. So it becomes a contest of which library had more scrutiny. I.e. you can compare a battle-tested library like cURL to stuff like baby's first XML parser. Plus, seeing how something is safe in practice is much different than just being memory safe.
> That went fast from "proven to be safe" to "yeah there are bugs".
Modulo compiler/hardware bugs goes without saying. Nothing really can exist in vacuum. You could prove your program is proven to work correctly, but if you put it on a platform where carry can randomly flip, and your perfect proof falls flat.
> Yes, unfixed for two years. I don’t have this bug in Go, for example, so why, as a Rust user, should I care whose fault it is?
Because it will be eventually fixed, unlike Go's design (that said they could change their tune and fix it, and I'll respectfully correct my statements). Then again, it's not like it is in scope for Rust. It's a bug in LLVM.
Plus, `unsound` bugs get extra scrutiny. Hell they had to write a new trait solver, Polonius, to solve problems some traits presented to the safety system.
> And how did you arrive at these numbers of "—"?
How many UBs do you leave open? How many errors other errors can your program prevent (e.g. do you allow `null`/`nil`)? And was this an error extremely obvious at time of writing (billion dollar mistake)?
Re: Partially Matching Zig Enums
#136Earlier quoted context omitted.
> Neither are memory safe, so if you're going by the "safe in practice" definition then it has to be verified experimentally. Hence - maybe. This is a ridiculous claim that it’s only "maybe". It’s so obvious, it’s like saying cars are not safe to drive, but if you use seatbelts and have airbags, then MAYBE they’re safer. I have verified this experimentally, like millions of other people. This argument is totally in b…
> This is a ridiculous claim that it’s only "maybe". It’s so obvious. Given enough effort, you can banish all UB and their related CVEs from a codebase. So it becomes a contest of which library had more scrutiny. I.e. you can compare a battle-tested library like cURL to stuff like baby's first XML parser. Plus, seeing how something is safe in practice is much different than just being memory safe. > That went fast fr…
Sure. With infinite energy, anything’s possible - we can prevent all bugs. The problem is, we don’t have infinite energy.
> So it becomes a contest of which library had more scrutiny. I.e. you can compare a battle-tested library like cURL to stuff like baby's first XML parser.
I agree that software varies in quality, and that different people and teams can produce very different levels of quality. The issue is that we’re talking about languages used by many different people with varying capabilities and levels of scrutiny.
What really annoys me is that my phone can get hit with an RCE just from someone sending me a message. That’s exactly the kind of vulnerability that happens because languages like C or C++ are so easy to misuse, due to their complexity and lack of safety. You just can’t compare that to Go or Rust, they’re in a completely different galaxy.
> How many UBs do you leave open? How many errors other errors can your program prevent (e.g. do you allow `null`/`nil`)? And was this an error extremely obvious at time of writing (billion dollar mistake)?
Everything is a trade off. I find Go to be a middle ground where I can offload much of the memory management complexity to the garbage collector, yet still have control over the aspects I care about, with acceptable performance for multi-threaded networking code.
I make extensive use of atomics and mutexes and I don’t need "fearless concurrency," because I can only recall one serious concurrency bug I’ve ever had (a data race) which took some debugging time to track down, but it wasn’t in Go. YMMV.
As for the “billion dollar mistake”, I understand the argument in the context of C or C++, but not in the context of Go. Once every few months, I get a nil pointer dereference notification from the monitoring stack. The middle network layer will report the error to the user, I fix it, and move on, a $0 mistake.
I used Rust in many other projects, where I would never use C++ or C. Rust has a higher cognitive load, more language complexity, and refactoring is painful but it’s a trade off.
Under Go’s memory model, there’s really only one form of undefined behavior: a program with a data race has no defined semantics. That’s pretty much it. Compare that to C or C++, it’s like I said, a different galaxy. I find the whole discussion around Go’s safety exaggerated, and more theoretical than what actually comes up in practice.
Re: Partially Matching Zig Enums
#137Earlier quoted context omitted.
> Maybe if someone bends over backwards to rationalize it, but not in any real sense. In a simple, real sense. Zig prevents out-of-bounds access just as Rust does; C++ doesn't. Interestingly, almost all of Rust's complexity is invested in the less dangerous kind of memory unsafety ( https://cwe.mitre.org/top25/archive/2024/2024_cwe_top25.html ). > You can't build RAII and moves into zig. So RAII is part of the defini…
C++ doesn't. Then why do my data structures detect if I go out of bounds? Interestingly, almost all of Rust's complexity is invested in the less dangerous kind of memory unsafety I didn't say anything about rust. So RAII is part of the definition of memory safety now? Yes. You can clean up memory allocations automatically with destructors and have value semantics for memory that is on the heap. Why not just declare m…
Because you have iterator debugging and/or assertions turned on and are only using non-primitive data structures (e.g. std::vector, std::array).
Zig does the thing that Rust and Go do where it makes the primary primitive for pointers to chunks of memory (slices) bounds checked. You can opt out with optimization settings, but I think most programs will build in "safe release" mode unless they're very confident in their test coverage.
It's strictly better than C++, because in practice codebases are passing lots of `(data, len)` params around no matter how strongly you emphasize in your style guide to use `std::span`. The path of least resistance in Zig, including the memory allocator interface, bundles in language-level bounds checking.
Re: Partially Matching Zig Enums
#138Earlier quoted context omitted.
> Maybe if someone bends over backwards to rationalize it, but not in any real sense. In a simple, real sense. Zig prevents out-of-bounds access just as Rust does; C++ doesn't. Interestingly, almost all of Rust's complexity is invested in the less dangerous kind of memory unsafety ( https://cwe.mitre.org/top25/archive/2024/2024_cwe_top25.html ). > You can't build RAII and moves into zig. So RAII is part of the defini…
C++ doesn't. Then why do my data structures detect if I go out of bounds? Interestingly, almost all of Rust's complexity is invested in the less dangerous kind of memory unsafety I didn't say anything about rust. So RAII is part of the definition of memory safety now? Yes. You can clean up memory allocations automatically with destructors and have value semantics for memory that is on the heap. Why not just declare m…
A problem not solved in C++ is the need to reserve a single bit-pattern per type that can be moved from, to indicate that it has been moved from (and is not a valid value for any other purpose).
Re: Partially Matching Zig Enums
#139Earlier quoted context omitted.
C++ doesn't. Then why do my data structures detect if I go out of bounds? Interestingly, almost all of Rust's complexity is invested in the less dangerous kind of memory unsafety I didn't say anything about rust. So RAII is part of the definition of memory safety now? Yes. You can clean up memory allocations automatically with destructors and have value semantics for memory that is on the heap. Why not just declare m…
> Then why do my data structures detect if I go out of bounds? Because you have iterator debugging and/or assertions turned on and are only using non-primitive data structures (e.g. std::vector, std::array). Zig does the thing that Rust and Go do where it makes the primary primitive for pointers to chunks of memory (slices) bounds checked. You can opt out with optimization settings, but I think most programs will bui…
Do you have any citations to support this 'safe release' theory? Like there are not many Zig applications and not many of them document their decisions. One i could find [1] does not mention safe anywhere.
Re: Partially Matching Zig Enums
#140Earlier quoted context omitted.
> This is a ridiculous claim that it’s only "maybe". It’s so obvious. Given enough effort, you can banish all UB and their related CVEs from a codebase. So it becomes a contest of which library had more scrutiny. I.e. you can compare a battle-tested library like cURL to stuff like baby's first XML parser. Plus, seeing how something is safe in practice is much different than just being memory safe. > That went fast fr…
> Given enough effort, you can banish all UB and their related CVEs from a codebase. Sure. With infinite energy, anything’s possible - we can prevent all bugs. The problem is, we don’t have infinite energy. > So it becomes a contest of which library had more scrutiny. I.e. you can compare a battle-tested library like cURL to stuff like baby's first XML parser. I agree that software varies in quality, and that differe…
You don't need infinite energy but it is a significant undertaking. The UB seem to obey a power law in regards to their lifetime i.e. every X years number of UB caused problems halves.
> I can only recall one serious concurrency bug I’ve ever had (a data race) which took some debugging time to track down, but it wasn’t in Go. YMMV.
That's what is nasty about UB in data races. It's not trivial to find and it's a pain in the neck to reproduce. So even taking you at your word, you not having issues with data races isn't the same as "I wrote code free of data races".
> As for the “billion dollar mistake”, I understand the argument in the context of C or C++, but not in the context of Go.
Even without a chance of UB. You're adding an implicit `Type | null` to every piece that uses nullable code without handling the null case. Each time you forget to do it, you cause either an UB or a null pointer error. And the place it manifests is different from where it's generated.
Furthermore, listening to Tony Hoare's talk (https://youtu.be/ybrQvs4x0Ps?t=1682), he mentions that trying to avoid the null, in the context of a language like Java or C#, that permits them is also causing people to waste time working around them.
> The middle network layer will report the error to the user, I fix it, and move on, a $0 mistake.
Sure, but that's not a $0 mistake. It's {time to fix * hourly rate}. Even if you're doing for OSS, you could have been spending that time doing something else.
> Everything is a trade off.
Sure. But trading "ease of use" for "preventing errors" is not something I want in any language I use. Null/nil are about as good concepts today as silently skipping errors.