Hacker News new | ask | show | jobs
by noodledoodletwo 1488 days ago
Cool article.

I can't figure out why i am supposed to care about zig beyond it's fun? I get that it's interesting and more safe than C (honestly though what the hell isn 't).

Say you write rust pretty regularly for new product development, what does zig offer to make my life better, my products more stable, etc?

6 comments

I write Rust for $day_job and Zig for fun. Rust is great, but if I had to rip on a few places where it's lacking for some applications: Zig maintains a small language footprint, is easier to interop with C, and makes it readily apparent when your code is doing anything non-trivial. To make that concrete:

- The small footprint in Zig enables fast compilation (and getting faster) and fast feedback cycles.

- Arbitrary nastiness can happen using From and other traits in Rust, and the tendency to shadow names and rely on type inference can make that unpleasant to track down. In Zig you'd be forced to make a choice (for any non-trivial type) at the return or call site of how you were going to convert things.

- If you want to use the stdlib and have any non-trivial control over how objects are allocated you're in for a rough time in Rust (say, a circular buffer backing some objects and a reusable arena for others). You'll probably be reinventing a lot of wheels.

- Similarly with issues like implicit locking in stdout. It's executing a syscall per line anyway, but the lock can inadvertently make contended writes 1000x slower, so suddenly logging in multithreaded code needs a dedicated logger, and not any of the common options since those fall back to the locking stdout we're trying to avoid.

Rust probably had a fairly small "language footprint" when it was only a few years old with no production use cases as well. I've used both in some small hobby projects. I like writing zig significantly more than rust, but we'll see how long the language remains small and compact.
On the contrary, it had a larger one because it had green threads which require an entire runtime (similar to Go).
I first tried rust in 2015[1]. It was my first serious attempt at rust.

The language was not small whatsoever at the time. It was not fun.

[1]: https://github.com/motiejus/makelua/tree/rust

I recommend trying it again. Consider how old the language is, 7 years is a really long time ago.

It may not be the smallest language, but, it compiles pretty tightly.

If zig catches on then the question may change from "why zig" to "do I really need rust?". Rust has a higher entry barrier and is harder to use daily. Is the safety guarantee worth it? In my experience, it is easier to fix a bug than to prove to the rust compiler that there is no bug. I just need a language that finds and reports the bugs (before production). Zig's error handling is interesting in this regard and it may be "good enough". Beyond that, I would prefer to spend my time on the real problem rather than appeasing the compiler.
It's really not that hard to use once you invest some time into it. I know where you are coming from, but appeasing the compiler actually means "I am writing safe code". Rather then "it compiled let's see what happens in production"
"some time" is the entry barrier I was referring to and increases the cost of onboarding and adoption. However, rust also incurs a non-negligible ongoing productivity cost for its complexity.

Rust can provide some guarantees but it still won't protect you from logic errors or a "clever" coworker. In comparison, zig is simple and at the end of the day readability is my best defense against bugs/errors.

There was an interesting comparison (and discussion) of zig vs rust safety a few months ago: https://news.ycombinator.com/item?id=26537693

I find myself agreeing with pron and others in that discussion when they say no one really wants to use a safe language. What they want are correct programs.

What remains to be seen is how easy it is to write correct programs in each and the values people place on the deltas between the two languages.

> rust also incurs a non-negligible ongoing productivity cost for its complexity

This isn't really consistent with my experience. Compared to writing other languages with varying levels of strictness (C, Python, Go to name a few), Rust's compiler saves me a lot of time writing tests and finding bugs.

It doesn't save me the work of fixing them, but in my experience, bugs that are hard to find and easy to fix drastically outnumber bugs that are easy to find and hard to fix.

That said, I haven't tried Zig. It's on my radar, but just haven't had a new project to start lately.

I am surprised that you don't encounter a productivity cost in rust vs languages with a gc (like python and go). If that were typical then I assume everyone would be using it. What do you consider to be the tradeoffs between rust and other languages? What are its drawbacks in your experience? How much rust experience do you have compared to other languages?
The productivity cost is mostly up front, once you understand borrowing it's seriously not bad in 99% of cases. Also, you can be initially productive in C or C++, but how much time goes into trying to fix undefined behavior, threading issues,etc. More importantly, when do those hours get spent? Is it after a customer is screaming into your support chatbot, or is it before you launch during normal development? To me that's the difference and the killer feature. That's why ms, aws, etc are using it. Not because it's new and marginally/debatably better then c/c++.

Once I started realizing what the borrow checker was saying it wasn't "fighting the compiler" I realized what I was asking the compiler to do was really not a good idea. It becomes your friend basically.

I didn't make this post to shill rust, I'm pretty language agnostic to my own detriment... The point is, it's worth considering and trying it again if you didn't get it the first time around. Rust took me 2x to become a tool I'd use, I honestly hated it at first.

I think a static borrow checker tool is very likely going to be in the future of zig ecosystem (unlikely to be in the mainline, but that's okay), especially after the intermediate representations stabilize.
I'll believe it when I see it hit 1.0 and has a healthy user base.
Most likely you specifically don't have much reason to care about Zig. Meanwhile, C can still often be found in areas where high performance and precise control over memory are important - such as in small embedded systems and game engine development - and Zig is a great fit as a replacement.

While you can technically use Rust in these domains you'll find yourself jumping through hoops and fighting against quirks that come with it being fairly high-level and very opinionated on how to enforce memory safety.

One such scenario I've encountered is implementing my own memcpy with a loop like `for i in 0..len`. This works in release builds, but without optimisations this gets a deeeeep callstack that eventually also calls memcpy, so you get a stack overflow. Note how memcpy is implemented in rlibc to avoid this issue: https://docs.rs/rlibc/latest/src/rlibc/lib.rs.html#30-38

Fair enough thanks for explaining. Basically if you want to make your own Malloc or things like it, zig is friendlier. Will say though, I've used rust for gamedev, and found the experience to be really nice. Was I writing my own memcpy though, nope.

It's funny how in rust you can write raw asm, but there seem to be quirks somewhere in the middle.

The Zig website [0] has an FAQ for this. I’ll copy in an abridged version here for convenience:

==========

Why Zig When There is Already C++, D, and Rust?

- No hidden control flow. If Zig code doesn’t look like it’s jumping away to call a function, then it isn’t.

- No hidden allocations. Zig has a hands-off approach when it comes to heap allocation. There is no new keyword or any other language feature that uses a heap allocator. The entire concept of the heap is managed by library and application code, not by the language.

- First-class support for no standard library. Zig has an entirely optional standard library. Each std lib API only gets compiled into your program if you use it. Zig has equal support for either linking against libc or not linking against it. Zig is friendly to bare-metal and high-performance development.

- A Portable Language for Libraries. Zig is attempting to become the new portable language for libraries by simultaneously making it straightforward to conform to the C ABI for external functions, and introducing safety and language design that prevents common bugs within the implementations.

- A Package Manager and Build System for Existing Projects. Not only can you write Zig code instead of C or C++ code, but you can use Zig as a replacement for autotools, cmake, make, scons, ninja, etc. And on top of this, it (will) provide a package manager for native dependencies. This build system is intended to be appropriate even if the entirety of a project’s codebase is in C or C++.

- Simplicity. Zig has no macros and no metaprogramming, yet is still powerful enough to express complex programs in a clear, non-repetitive way. Even Rust has macros with special cases like format!, which is implemented in the compiler itself. Meanwhile in Zig, the equivalent function is implemented in the standard library with no special case code in the compiler.

- Tooling. Zig provides binary archives for Linux, Windows, macOS and FreeBSD. It is installed by downloading and extracting a single archive, no system configuration needed. It is statically compiled, uses LLVM, has out of the box cross-compilation to most major platforms, and ships w/ libc source and dynamically compiles when needed. The Zig build system has caching and compiles C and C++ code with libc support

==========

[0] https://ziglang.org/learn/why_zig_rust_d_cpp/

> Simplicity. Zig has no macros and no metaprogramming

This one is an odd point; this very article is a demonstration of metaprogramming*:

> Thanks to Zig’s type reflection we can read a row of data into a user-provided type without needing to write any “mapping” function: we know the type we want to read (here the User struct) and can analyse it at compile-time.

* Which is a feature I favor, for the record.

I think the poster meant "no macros" (and especially no c-style lexical macros)
No metaprogramming as in no separate metalanguage (e.g. templates, macro_rules!)
Homogeneous metaprogramming is a thing...
Yea I feel like most of these differentiators aren't things most people care about barring one. Tight integrations with c/c++ is potentially useful, beyond that I don't really get it. It's kind of like Hare in that regard?
In general those are the reasons I see people give for why they still use C instead of C++ or newer languages. Also all of those things were, and maybe still are necessary for embedded systems where C still dominates.

To me it comes down to Zig is a "modern" C, but unlike most other attempts at replacing C it doesn't skip some of C's use cases.

See I got downvoted a bit... Let me rephrase, other modern languages offer something along the lines of these, of the things that aren't there for say rust, close integration with c/c++ is nice. Ie rust has nostd, etc.
out of that list, rust doesn't offer "no macros and no metaprogramming" and "no hidden control flow". maybe these just aren't things you care about?
Yea but for the most part you can avoid macros, and areas where you can't you can consider them to be keywords in my opinion anyways. Macros aren't all bad, but they do get abused a lot and make a nightmare for others. Fwiw Ive only written one macro and it was for learning purposes only.
Yes, but say you are not yet writing Rust, perhaps you should choose Zig instead of it?
I think you're both asking the same question from different angles. If I understand correctly, and I could be misremembering but Zig is supposed to be compatible with C++ not just C, which is something that is not necessarily straight forward in other C / C++ competing languages. I hear even D has some issues with mangling and what not.

In all honesty, I prefer the syntax of D over all the others, it feels the most like Java or C# but with a lot of modern benefits. D is trying to do too much though it feels like and I would love for the next D standard library to support OOTB similar to what Go supports, especially a very minimalist web server, I think all modern programming languages should be capable of spinning up web servers out of the box. This is one small detail Go got right in my opinion.

Here's an article from the Chromium team on challenges they faced with trying to integrate Rust (or at least evaluating it) note the entry was last updated in 2020:

https://www.chromium.org/Home/chromium-security/memory-safet...

D nim and a few others offer nicer syntax over what's normally C for sure. Yea it doesn't surprise me that an established product had trouble incorporating rust, but at the same time, there's a reason why they went through with it right? Memory safety, no data races, etc. Like there is a motivating reason for using it. Meanwhile lots of other products have found ways of integrating Rust and some of them are risk averse products.
I like that the web server in Go's standard library comes as a library, so you can embed a web server into your application, even if it otherwise isn't web-related at all, just to allow for some introspection and/or control.

(To be fair, I think Python and Ruby have HTTP servers in their standard libraries as well, at least minimalist ones.)

I see where you are coming from, zig is easier to learn, but zig doesn't offer what rust does with respect to safety. That feature is so hard for me to ignore. I respect it though, some people want to be up and running with a new technology in a day or whatever, rust doesn't give you that unless you are very seasoned.
Honestly many applications don't need the level of safety that rust provides (for small single threaded cli apps or cloud lambdas, just allocate into an arena and throw everything away when the program quits, no UAF or DF because you're never freeing)

At the other extreme, if you're writing an operating system or a language VM, you probably want contextual allocators (like an allocator that takes a runtime argument like "which green thread I'm allocating on") which rust makes extremely difficult.

If you really need memory and resource safety I think the best answer is to be patient. Zig is very easy to parse and I imagine it will be the case that static analysis build tools will come about which can do what you want out of rust.... Being decoupled from the compiler chain you would be able to run fast but guard your prs to main/dev/release (as you see fit) with static analysis tools that will protect you with the safety you seek in an isomorphic fashion to "how rust does it". There's no reason why someone couldn't write it now, but with a lot of things (like ZIR/AIR) being highly unstable -- and these are what you're likely to want to statically analyze for such a tool -- for ones sanity I don't recommend building out a tool like that now.

I actually disagree with that and think it encourages bad practices in people who don't know better. The rise of fuzz testing on these old c applications following this kind of mentality is a constant source of cves . Why expose users to problems, if it's easy not too?

Also it's not like that safety costs much of anything. You really can get comfortable writing rust, especially "easy rust" like a single thread cli app. It really is easy, even convenient.

Either zig is a different tool to rust, in which case I get it. Or people should be waiting for zig to be mature, meanwhile rust has been stable for half a decade, and at this point "just works". I'm not going to sit through an addendum static analyzer and all the bugs that come with making one for a few years. Other people whose appetite for risk is higher will though, and I wish them the best. I've seen efforts in other languages, my conclusion is that it's much better to have it built in...

As someone who uses both for different personal projects:

- I use zig as my build system for both rust, zig, C libraries and linking since the build system works really well for this purpose

- When I need to write applications or libraries that can benefit from compile-time code, I always try and use zigs since it's much easier to use comptime then a combination of rust macros and generics

- I like the zig async story a lot better. Or at least it's much easier to wrap my head around and write code in compared to rust + tokyo

On the other hand, sometimes I know a project will benefit from the borrow checker or I want to use some of the awesome rust crates that the community made and I'll use rust instead.

Does a really short summary of this read kind of like, "zig is a modern c, but I like using rust as a modern c++"?

Agree, Tokio is a little tricky for people new to rust no doubt, but it's tricky for a reason. It's saving lives in production.

Just FYI: things that are actually "saving lives" are probably realtime applications, which need things like deterministic execution time, no allocation from the os, bounded memory usage, which rust generally does not give you without a ton of effort.
This is actually the frame of reference I've taken to giving most folks. Zig is to C what Rust is to C++ in my mind. Is there a lot of overlap between all 4? Absolutely. But devs still choose C over C++ (and Rust) in some cases, and I think it'll be similar for Zig
Thanks for explaining, I really appreciate it.
Yes, but Rust offers something more than just modernizing C++. The borrow checker provides something entirely new that has the potential to make a huge impact.
Just semantics, but I think the borrow checker is part of modernizing C++.

(The way to manage memory in C++ and C is to opt in to a memory management pattern... hopefully you've chosen a good pattern and hopefully you follow it consistently. Patterns are generally backed by utilities and primitives that are hopefully correct, complete, and hopefully make it relatively easy to follow the pattern consistently. Hopefully you can get a linter to help you too. In a sense Rust takes the same approach, but was implemented from the ground up to eliminate all the gaps so you essentially don't have to hope to successfully bridge the gaps yourself.)

Can you elaborate on the savings lives part? I sense a good story, would love to hear.