62 comments

[ 10.3 ms ] story [ 137 ms ] thread
As far as my understanding of zig goes....it can compile into C....so if you really want secure C code you can compile zig into C?
"All it took was some basic understanding of memory management and a bit of discipline."

The words of every C programmer who created a CVE.

> The words of every C programmer who created a CVE.

Much of Zig's user base seems to be people new to systems programming. Coming from a managed code background, writing native code feels like being a powerful wizard casting fireball everywhere. After you write a few unsafe programs without anything going obviously wrong, you feel invincible. You start to think the people crowing about memory safety are doing it because they're stupid, or, cowards, or both. You find it easy to allocate and deallocate when needed: "just" use defer, right? Therefore, it someone screws up, that's a personal fault. You're just better, right?

You know who used to think that way?

Doctors.

Ignaz Semmelweis famously discovered that hand-washing before childbirth decreased morality by an order of magnitude. He died poor and locked in an asylum because doctors of the day were too proud to acknowledge the need to adopt safety measures. If mandatory pre-surgical hand-washing step prevented complication, that implied the surgeon had a deficiency in cleanliness and diligence, right?

So they demonized Semmelweis and patients continued for decades to die needlessly. I'm sure that if those doctors had been on the internet today, they would say, as the Zig people do say, "skill issue".

It takes a lot of maturity to accept that even the most skilled practitioners of an art need safety measures.

What are your thoughts on nim, odin and v-lang, D-lang?

I feel like I am most interested about nim given how easy it was to pick up and how interoperable it is with C and it has a garbage collector and can change it which seems to be great for someone like me who doesn't want to worry about manual memory management right now but maybe if it becomes a bottleneck later, I can atleast fix it without worrying too much..

IMO, as a C++ developer, Swift makes the most sense to me if I were looking for a safer alternative.

I think people prefer what's familiar to them, and Swift definitely looks closer to existing C++ to me, and I believe has multiple people from the C++ WG working on it now as well, supposedly after getting fed up with the lack of language progress on C++.

The most recent versions gained a lot in the way of cross-platform availability, but the lack of a native UI framework and its association with Apple seem to put off a lot of people from even trying it.

I wish it was a lot more popular outside of the Apple ecosystem.

(comment deleted)
> So when it comes to memory management there are two [THREE] terms you really need to know, [THE REGISTER BANK,] the stack and the heap.

Edits mine.

I like to keep the spacetime topologies complete.

Constant = time atom of value.

Register = time sequence of values.

Stack = time hierarchy of values.

Heap = time graph of values.

The benefit of Zig seems to be that it allows you to keep thinking like a C programmer. That may be great, but to a certain extent it’s also just a question of habit.

Seasoned Rust coders don’t spend time fighting the borrow checker - their code is already written in a way that just works. Once you’ve been using Rust for a while, you don’t have to “restructure” your code to please the borrow checker, because you’ve already thought about “oh, these two variables need to be mutated concurrently, so I’ll store them separately”.

The “object soup” is a particular approach that won’t work well in Rust, but it’s not a fundamentally easier approach than the alternatives, outside of familiarity.

> Seasoned Rust coders don’t spend time fighting the borrow checker - their code is already written in a way that just works.

I really wish people would quit bleating on about the borrow checker. As someone who does systems programming, that's not the problem with Rust.

Which Trait do I need to do X? Where is Trait Y and who has my destructor? How am I supposed to refactor this closure into a function? Sigh, I have to wrap yet another object as a newtype because of the Orphan Rule. Ah yes, an eight deep chain initialization calls because Rust won't do named/optional function arguments. Oh, great, the bug is inside a macro--well, there goes at least one full day. Ah, an entity component systems that treats indices like pointers but without the help of the compiler so I can index into the void and scribble over everything--but, hey, it's memory safe and won't segfault (erm, there is a reason why C programmers groan when they get a stack/heap smasher bug).

I want to like Zig, but D still exists and feels like everything I want from a C-like alternative to C++ I just wish the rest of the industry had adopted it long ago. Zig has a strange syntax, and Rust is basically eating chunks of the industry, especially in programmer tooling across various languages as is Go (it powers most cloud providers and is the 2nd top choice for AI right after Python).
Because it doesn't actually enforce anything and lets you blow your foot off just like C?
Why does a personal blog need so many advertising options?
building houses or power lines without regulations feels more practical as well
I was going to say that it's greatly understating the value of the borrow checker. It guarantees no invalid memory accesses. But then it added:

> This means that basically the borrow checker can only catch issues at comptime but it will not fix the underlying issue that is developers misunderstanding memory lifetimes or overcomplicated ownership. The compiler can only enforce the rules you’re trying to follow; it can’t teach you good patterns, and it won’t save you from bad design choices.

In the short times that I wrote Rust, it never occurred to me that my lifetime annotations were incorrect. They felt like a bit of a chore but I thought said what I meant. I'm sure there's a lot of getting used to using it--like static types--and becomes second nature at some point. Regardless, code that doesn't use unsafe can't have two threads concurrently writing the same memory.

The full title is "Why Zig Feels More Practical Than Rust for Real-World CLI Tools". I don't see why CLI tools are special in any respect. The article does make some good points, but it doesn't invalidate the strength of Rust in preventing CVEs IMO. Rust or Zig may feel certain ways to use for certain people, time and data will tell.

Personally, there isn't much I do that needs the full speed of C/C++, Zig, Rust so there's plenty of GC languages. And when I do contribute to other projects, I don't get to choose the language and would be happy to use Rust, Zig, or C/C++.

I also loved Zig when manually typing code, but I increasingly use AI to write my code even in personal projects. In that context, I'd rather use Rust more, since the AI takes care of complex syntax anyway. Also, the rust ecosystem is bigger, so I'd rather stick to this community.

> Developers are not Idiots

I'm often distracted and AIs are idiots, so a stricter language can keep both me and AIs from doing extra dumb stuff.

> I'm often distracted

I really appreciate this in my role, where I have an office right next to the entrance to the building. I get walk-ins all of the time. When my door is closed, I get knocks on the door all of the time. Both AI and strict languages are great tools in my environment, where focus for me is as abundant as water in a desert.

Aren't these two points contradictory? Forgive me if I'm misunderstanding.

> Rust’s borrow checker is a a pretty powerful tool that helps ensure memory safety during compile time. It enforces a set of rules that govern how references to data can be used, preventing common programming memory safety errors such as null pointer dereferencing, dangling pointers and so on. However you may have notice the word compile time in the previous sentence. Now if you got any experience at systems programming you will know that compile time and runtime are two very different things. Basically compile time is when your code is being translated into machine code that the computer can understand, while runtime is when the program is actually running and executing its instructions. The borrow checker operates during compile time, which means that it can only catch memory safety issues that can be determined statically, before the program is actually run. > > This means that basically the borrow checker can only catch issues at comptime but it will not fix the underlying issue that is developers misunderstanding memory lifetimes or overcomplicated ownership. The compiler can only enforce the rules you’re trying to follow; it can’t teach you good patterns, and it won’t save you from bad design choices.

This appears to be claiming that Rust's borrow checker is only useful for preventing a subset of memory safety errors, those which can be statically analysed. Implying the existence of a non-trivial quantity of memory safety errors that slip through the net.

> The borrow checker blocks you the moment you try to add a new note while also holding references to the existing ones. Mutability and borrowing collide, lifetimes show up, and suddenly you’re restructuring your code around the compiler instead of the actual problem.

Whereas this is only A Thing because Rust enforces rules so that memory safety errors can be statically analysed and therefore the first problem isn't really a problem. (Of course you can still have memory safety problems if you try hard enough, especially if you start using `unsafe`, but it does go out of its way to "save you from bad design choices" within that context.)

If you don't want that feature, then it's not a benefit. But if you do, it is. The downside is that there will be a proportion of all possible solutions that are almost certainly safe, but will be rejected by the compiler because it can't be 100% sure that it is safe.

This is a really bad take, on par with the "we don't need types" post from last week.

The thing I wish we would remember, as developers, is that not all programs need to be so "safe". They really, truly don't. We all grew up loving lots of unsafe software. Star Fox 64, MS Paint, FruityLoops... the sad truth is that developers are so job-pilled and have pager-trauma, so they don't even remember why they got in the game.

I remember reading somewhere that Andrew Kelley wrote zig because he didn't have a good language to write a DAW in, and I think its so well suited to stuff like that! Make cool creative software you like in zig, and people that get hella about memory bugs can stay mad.

Meanwhile, everyone knows that memory bugs made super mario world better, not worse.

    The thing I wish we would remember, as developers, is that not all programs need to be so "safe".
"Safety" is just a shorthand for "my program means what I say". Unsafety is semantic gibberish.

There's lots of reasons to write artistically gibberish code, just as there is with natural language (e.g. Lewis Carroll). Most programs aren't going for code as art though. They're trying to accomplish something definite through a computer and gibberish is directly counterproductive. If you don't mean what you write or care what you get, software seems like the wrong way to accomplish your goals. I'd still question whether you want software even in a probabilistic argument along these lines.

Even for those cases where gibberish is meaningful at a higher level (like IOCCC and poetry), it should be intentional and very carefully crafted. You can use escape hatches to accomplish this in Rust, though I make no comment on the artistic merits of doing so.

The argument you're making is that uncontrolled, unintentional gibberish is a positive attribute. I find that a difficult argument to accept. If we could wave a magic wand and make all code safe with no downsides, who among us wouldn't?

It doesn't change anything about Super Mario World speedruns because you can accomplish the same thing as arbitrary code execution inputs with binary patching. We just have this semi-irrational belief that one is cheating and one is not.

This article seems pretty confused about what the borrow checker does or does not do - I've never heard compile time enforcement listed as a negative of the borrow checker before. It might do the author good to try writing some (non-trivial) memory management in both Zig and Rust some time.
when I think of rust I think of beauties like this,

self.last.as_ref().unwrap().borrow().next.as_ref().unwrap().clone()

I know it can be improved but that's what I think of

Without any context it’s impossible to tell if code needs to be that obtuse or if uou somehow built your way into a setup that you could’ve avoided. ;P
The fact that your app crashes when you run out of stack is a compiler bug, not a feature. Memory is memory. The fact that languages in the 40s split in it to stack and heap, doesn't make it a foundational mathematical law.

Yes, safety isn't correctness but if you can't even get safety then how are you supposed to get correctness?

For small apps Zig probably is more practical than Rust. Just like hiring an architect and structural engineers for a fence in your back yard is less practical than winging it.

No shade here, just a genuine question: why run ads on a blog like this? A personal technical blog probably doesn't get a ton of traffic. So what's the point? I'm honestly curious.
Actually, developers are idiots. Everyone is. Some just don't know it or won't admit it.

I once joined a company with a large C/C++ codebase. There I worked with some genuinely expert developers - people who were undeniably smart and deeply experienced. I'm not exaggerating and mean it.

But when I enabled the compiler warnings (which annoyed them) they had disabled and ran a static analyzer over the codebase for the first time, hundreds of classic C bugs popped up: memory leaks, potential heap corruptions, out-of-bounds array accesses, you name it.

And yet, these same people pushed back when I introduced things like libfmt to replace printf, or suggested unique_ptr and vector instead of new and malloc.

I kept hearing:

"People just need to be disciplined allocations. std::unique_ptr has bad performance" "My implementation is more optimized than some std algorithm." "This printf is more readable than that libfmt stuff." etc.

The fact is, developers, especially the smart ones probably, need to be prevented from making avoidable mistakes. You're building software that processes medical data. Or steers a car. Your promise to "pay attention" and "be careful" cannot be the safeguard against catastrophe.

If I had a penny for every time I heard that devs are not idiots, I'd be billionaire.

It's true, but devs are not infallible and that's the point of Rust. Not idiots, not infallible either.

IMO admitting that one can make mistakes even if they don't think they have is a sign of an experienced and trustworthy developer.

It's not that Rust compiler engineers think that devs are idiots, in fact you CAN have footguns in Rust, but one should never use a footgun easily, because that's how you get security vulnerabilities.

Ah, sweet flame war fuel.

Maybe we'll even get a tabs vs. spaces article next.

This blog is atrocious from an ad standpoint and the recent flood of posts feels promotional and intentionally controversial. The articles are also devoid of any interesting perspectives. Are people actually reading this?