Hacker News new | ask | show | jobs
by obviouslynotme 217 days ago
There is nothing new under the Sun. However, some languages manifest as good rewrites of older languages. Rust is that for C++. Zig is that for C.

Rust is the small, beautiful language hiding inside of Modern C++. Ownership isn't new. It's the core tenet of RAII. Rust just pulls it out of the backwards-compatible kitchen sink and builds it into the type system. Rust is worth learning just so that you can fully experience that lens of software development.

Zig is Modern C development encapsulated in a new language. Most importantly, it dodges Rust and C++'s biggest mistake, not passing allocators into containers and functions. All realtime development has to rewrite their entire standard libraries, like with the EASTL.

On top of the great standard library design, you get comptime, native build scripts, (err)defer, error sets, builtin simd, and tons of other small but important ideas. It's just a really good language that knows exactly what it is and who its audience is.

5 comments

I think that describing Zig as a "rewrite of C" (good or otherwise) is as helpful as describing Python as a rewrite of Fortran. Zig does share some things with C - the language is simple and values explicitness - but at its core is one of the most sophisticated (and novel) programming primitives we've ever seen: A general and flexible partial evaluation engine with access to reflection. That makes the similarities to C rather superficial. After all, Zig is as expressive as C++.

> Most importantly, it dodges Rust and C++'s biggest mistake, not passing allocators into containers and functions

I think that is just a symptom of a broader mistake made by C++ and shared by Rust, which is a belief (that was, perhaps, reasonable in the eighties) that we could and should have a language that's good for both low-level and high-level programming, and that resulted in compromises that disappoint both goals.

To me, the fact that Zig has spent so long in development disqualifies it as being a "rewrite of C."

To be clear, I really like Zig. But C is also a relatively simple language to both understand and implement because it doesn't have many features, and the features it does have aren't overly clever. Zig is a pretty easy language to learn, but the presence of comptime ratchets up the implementation difficulty significantly.

A true C successor might be something like Odin. I am admittedly not as tuned into the Odin language as I am Zig, but I get the impression that despite being started six months after Zig, the language is mostly fully implemented as envisioned, and most of the work is now spent polishing the compiler and building out the standard library, tooling and package ecosystem.

I don't think it's the implementation that's delaying Zig's stabilisation, but the design. I'm also not sure comptime makes the implementation all that complicated. Lisp macros are more powerful than comptime (comptime is weaker by design) and they don't make Lisp implementation complicated.
Fair. I'm not a compiler developer, so I'll defer to your expertise on that front.

That being said, I suppose my ultimate wonder is how small a Zig implementation could possibly be, if code size and implementation simplicity was the priority. In other words, could a hypothetical version of the Zig language have existed in the 80's or 90's, or was such a language simply out of reach of the computers of the time.

It's not quite as minimal as C, but it definitely could have been made in the 80s or 90s (actually, 70s, too) :) There were far larger, more complex languages back then, including low-level languages such as C++ and Ada, not to mention even bigger high-level languages. High-level languages were already more elaborate even in the 70s (comptime is no more tricky than macro or other meta-programming facilities used in Lisp in the sixties or Smalltalk in the 70s; it certainly doesn't come even remotely close to the sophistication of 1970s Prolog).

I don't think there's any programming language today that couldn't have been implemented in the 90s, unless the language relies on LLMs.

> Zig does share some things with C - the language is simple and values explicitness - but at its core is one of the most sophisticated (and novel) programming primitives we've ever seen: A general and flexible partial evaluation engine with access to reflection.

To my understanding (and I still haven’t used Zig) the “comptime” inherently (for sufficiently complex cases) leads to library code that needs to be actively tested for potential client use since the instantiation might fail. Which is not the case for the strict subset of “compile time” functionality that Java generics and whatnot bring.

I don’t want that in any “the new X” language. Maybe for experimental languages. But not for Rust or Zig or any other that tries to improve on the mainstream (of whatever nice) status quo.

> leads to library code that needs to be actively tested for potential client use since the instantiation might fail

True, like templates in C++ or macros in C or Rust. Although the code is "tested" at compile time, so at worst your compilation will fail.

> I don’t want that in any “the new X” language

Okay, and I don't want any problem of any kind in my language, but unfortunately, there are tradeoffs in programming language design. So the question is what you're getting in exchange for this problem. The answer is that you're getting a language that's both small and easy to inspect and understand. So you can pick having other problems in exchange for not having this one, but you can't pick no problems at all. In fact, you'll often get some variant of this very problem.

In Java, you can get by with high-level abstractions because we have a JIT, but performance in languages that are compiled AOT is more complicated. So, in addition to generics, low-level languages have other features that are not needed in Java. C++ has templates, which are a little more general than generics, but they can fail to instantiate, too. It also has preprocessor macros that can fail to compile in a client program. Rust has ordinary generics, which are checked once, but since that's not enough for a low-level language, it also has macros, and those can also fail to expand correctly.

So in practice, you either have one feature that can fail to compile in the client, or you can have the functionality split among multiple features, resulting in a more complicated language, and still have some of those features exhibit the same problem.

I wasn’t clear then. I would rather have N language features of increasing complexity/UX issues for dealing with increasingly complex situations rather than one mechanism to rule them all that can fail to instantiate in all cases (of whatever complexity). That’s the tradeoff that I want.

Why? Because that leads to better ergonomics for me, in my experience. When library authors can polish the interface with the least powerful mechanism with the best guarantees, I can use it, misuse it, and get decent error messages.

What I want out of partial evaluation is just the boring 90’s technology of generalized “constant folding”.[1] I in principle don’t care if it is used to implement other things... as long as I don’t have surprising instantiation problems when using library code that perhaps the library author did not anticipate.

[1]: And Rust’s “const” approach is probably too limited at this stage. For my tastes. But the fallout of generalizing is not my problem so who am I to judge.

> Okay, and I don't want any problem of any kind in my language, but unfortunately, there are tradeoffs in programming language design.

I see.

> So in practice, you either have one feature that can fail to compile in the client, or you can have the functionality split among multiple features, resulting in a more complicated language,

In my experience Rust being complicated is more of a problem for rustc contributors than it is for me.

> and still have some of those features exhibit the same problem.

Which you only use when you need them.

(I of course indirectly use macros since the standard library is full of them. At least those are nice enough to use. But I might have gotten some weird expansions before, though?)

That will have to do until there comes along a language where you can write anything interesting as library code and still expose a nice to use interface.

> I would rather have N language features of increasing complexity/UX issues for dealing with increasingly complex situations rather than one mechanism to rule them all that can fail to instantiate in all cases (of whatever complexity). That’s the tradeoff that I want.

It's not that that single mechanism can fail in all situations. It's very unlikely to fail to compile in situations where the complicated language always compiles, and more likely to fail to compile when used for more complicated things, where macros may fail to compile, too.

It's probability of compilation failure is about the same as that of C++ templates [1]. Yeah, I've seen compilation bugs in templates, but I don't think that's on any C++ programmer's top ten problem list (and those bugs are usually when you start doing stranger things). Given that there can be runtime failures, which are far more dangerous than compilation failures and cannot be prevented, that the much less problematic compilation failures cannot always be prevented is a pretty small deal.

But okay, we all prefer different tradeoffs. That's why different languages choose design philosophies that appeal to different people.

[1]: It's basically a generalisation of the same idea, only with better error messages and much simpler code.

I don't know man, Rust's borrowing semantics are pretty new under the sun, and actually do change the way you think about software. It's a pretty momentous paradigm shift.

Zig is nice too, but it's not that.

To call Rust syntax beautiful is a stretch. It seems that way in the beginning but then quickly devolves into a monstrosity when you start doing more complex things.

Zig on the other specifically addresses syntax shortcomings in part of C. And it does it well. That claim of rust making C more safe because it’s more readable applies to Zig more than it does to Rust.

I feel like the reason the rust zealots lobby like crazy to embed rust everywhere is twofold. One is that they genuinely believe in it and the other is that they know that if other languages that address one of the main rust claims without all the cruft gains popularity they lose the chance of being permanently embdedded in places like the kernel. Because once they’re in it’s a decade long job market

> they know that if other languages that address one of the main rust claims without all the cruft gains popularity they lose the chance of being permanently embdedded in places like the kernel

First of all, I'm really opposed to saying "the kernel". I am sure you're talking about the Linux kernel, but there are other kernels (BSD, Windows etc.) that are certainly big enough to not call it "the" kernel, and that may also have their own completely separate "rust-stories".

Secondly, I think the logic behind this makes no sense, primarily because Rust at this point is 10 years old from stable and almost 20 years old from initial release; the adoption into the Linux kernel wasn't exactly rushed. Even if it was, why would Rust adoption in the Linux kernel exclude adoption of another language as well, or a switch to another, if it's better? The fact that Rust was accepted at all to begin with aside from C disproves the assumption, because clearly that kernel is open for "better" languages.

The _simplest_ explanation to why Rust has succeeded is that it's solves actual problems, not that "zealots" are lobbying for it to ensure they "have a job".

> Rust at this point is 10 years old from stable

Rust is not stable even today! There is no spec, no alternative implementations, no test suite... "Stable" is what "current compiler compiles"! Existing code may stop compiling any day....

Maybe in 10 years it may become stable, like other "booring" languages (Golang and Java).

Rust stability is why Linus opposes its integration into kernel.

In the "other good news department", GCC is adding a Rust frontend to provide the alternative implementation, and I believe Rust guys accepted to write a specification for the language.

I'm waiting for gccrs to start using the language, actually.

> To call Rust syntax beautiful is a stretch.

I don’t see where the comment you’re replying to does that (was it edited?). Their comment says nothing about aesthetics.

Higher up

> Rust is the small, beautiful language hiding inside of Modern C++

That says Rust is beautiful, not its syntax.
> To call Rust syntax beautiful is a stretch.

I'm no Rust fan, but beauty of a syntax is always in the eye of the beholder.

I personally find Go, C++ and Python's syntax beautiful. All can be written in very explicit or expressive forms. On the other hand, you can hide complexity to a point.

If you are going to do complex things in a compact space, you'll asymptotically approach Perl or PCRE. It's maths.

All code is maths, BTW.

> if other languages that address one of the main rust claims without all the cruft

But regardless of how much one likes Zig, it addresses none of the problems that Rust seeks to solve. It's not a replacement for Rust at all, and isn't suitable for any of the domains where Rust excels.

> and isn't suitable for any of the domains where Rust excels.

That's a pretty bold claim since Zig is specifically designed for systems programming, low level stuff, network services, databases (think Tigerbeetle). It's not memory safe like Rust is, but it comes with constructs that make it simple to build largely memory safe programs.

> It's not memory safe like Rust is, but it comes with constructs that make it simple to build largely memory safe programs.

Right, this is the specific important thing that Rust does that Zig doesn't (with the caveat that Rust includes the `unsafe` mechanism - as a marked, non-default option - specifically to allow for necessary low-level memory manipulation that can't be checked for correctness by the compiler). Being able to guarantee that something can't happen is more valuable than making it simple to do something correctly most of the time.

It's not that simple though, Zig has equivalent spatial memory safety which prevents issues that are pretty consistently among (or at) the top of the list for most dangerous vulnerability classes.

And while I don't have enough experience with Rust to claim this first hand, my understanding is that writing correct unsafe Rust code is at least an order of magnitude harder than writing correct Zig code due to all of the properties/invariants that you have to preserve. So it comes with serious drawbacks, it's not just a quick "opt out of the safety for a bit" switch.

> Being able to guarantee that something can't happen is more valuable than making it simple to do something correctly most of the time.

Of course, all other things being equal, but they're not.

Sure but there's this belief in the Rust community that it's not responsible anymore to write software that isn't memory safe on the same level as Rust.

So Zig would fail that, but then you could also consider C++ unsuitable for production software - and we know it clearly is still suitable.

I predict Zig will just become more and more popular (and with better, although not as complete- memory safety), and be applied to mission critical infra.

Given the density of memory issues in the Bun issue tracker I have a hard time squaring the statement that Zig makes it "easy" to build memory safe programs.

https://github.com/oven-sh/bun/issues?q=segfault%20OR%20segm...

> https://github.com/oven-sh/bun/issues?q=segfault%20OR%20segm...

It should be noted that most of those issues are created by opening a link that bun creates when it crashes, they are not yet reviewed/confirmed and most likely a lot of them are dulplicates of the same issue.

Rust is not designed for low level system programming / embedded systems like Zig is. It is designed to make a browser and software that share requirements with making a browser.

There is some overlap but that's still different. The Zig approach to memory safety is to make everything explicit, it is good in a constrained environment typical of embedded programming. The Rust approach is the opposite, you don't really see what is happening, but there are mechanisms to keep your safe. It is good for complex software with lots of moving parts in an unconstrained environment, like a browser.

For a footgun analogy, one will hand you a gun that will never go off unless you aim and pull the trigger, so you can shoot your foot, but no sane person will. It is a good sniper rifle. The Rust gun can go off at any time, even when you don't expect it, but it is designed in such a way that it will never happen when it is pointed at your foot, even if you aim it there. It is a good machine gun.

> Rust is not designed for low level system programming / embedded systems like Zig is.

Pray tell, with Rust already being used in kernels, drivers, and embedded what makes Zig better suited for low-level systems?

More chance to explode a UB in your hand? For that, there is C.

For my part, I don't know why, but Zig's syntax feels wrong to me. I don't even know why. I really want to like its syntax, as Zig seems really promising to me, but I just don't, which makes it not very enjoyable for me to write.

I don't know if it's my lack of practice, but I never felt the same about, say, Rust's syntax, or the syntax of any other language for that matter.

> I feel like

Needless flame bait follows.

Kind of, https://en.wikipedia.org/wiki/Cyclone_(programming_language)

What it has achieved is making affine types something mainstream developers would care about.

That kind of is a bit load bearing. The differences are pretty huge. Plus, borrow checker is nowhere to be found. Cyclone is more C with a few tweaks (tagged unions, generics, regions, etc.).
Borrow checking is basically a synonym for affine type system.

The same outcome can be achieved via affine types, linear types, effects, dependent types, regions, proofs, among many other CS research in type systems.

Which is why following Rust's success, plenty of managed languages are now going through the evolution step to combine automatic resource management with improved type systems.

Taking the one that best approaches their current design.

> Borrow checking is basically a synonym for affine type system.

No? It's more akin to flow analysis with special generic types called lifetimes.

> The same outcome can be achieved via affine types, linear types, effects, dependent types, regions, proofs, among many other CS research in type systems.

Sure, and sounds, colors, and instruments are the same, but they are mixed to create an audio-video song. I'm not saying that what Rust did is something that came about ex nihilo, without precedence.

But having it all unified uniquely the way Rust did it is frankly revolutionary. Until now, people assumed if you want memory safety, you have to add a GC (tracing or RC). Or alternatively write extensive proofs about types like Ada/Spark.

There were languages with lifetimes and borrowing mechanics before Rust. Rust packages these mechanics in a nice way. Just like Zig encodes many niceties in a useful C language (comptime, simple cross-compilation, stdlib).
Which ones?? Before Rust, to my knowledge, no language had an actually practical way to use lifetimes and borrow-checking so that both memory safety and concurrency safety (data races, which is huge) were solved, even though the concepts were known in research. Doing the actual work to make it practical is what makes the difference between some obscure research topic and a widely used language that actually solves serious problems in the real world.
Cyclone for one, which AT&T created exactly to replace C.
Yeah but is that a practical language people can use instead of C and Rust? I’ve always heard of it only as a research language that inspired rust but nothing else.
Outside AT&T until they ramped down the project, I guess not, Rust also took its time to actually take off beyond Mozilla, and is around because it was rescued by big tech (Amazon, Google, Microsoft,...) hiring most of the core team after Mozilla's layoffs.
> actually do change the way you think about software. It's a pretty momentous paradigm shift.

That's true for people who doesn't read and think about the code they write. For people who think from the perspective of a computer, Rust is "same checks, but forced by the compiler".

Make no mistake, to err is human, but Rust doesn't excite me that much.

> Most importantly, it dodges Rust and C++'s biggest mistake, not passing allocators into containers and functions

Funny. This was a great sell to me. I wonder why it isn’t the blurb. Maybe it isn’t a great sell to others.

The problem for me with so many of these languages is that they’re always eager to teach you how to write a loop when I couldn’t care less and would rather see the juice.

However, nowadays with comprehensive books like this, LLM tools can better produce good results for me as I try it out.

Thank you.

Very, very few people outside of foundational system software, HFT shops, and game studios understand why it's a great selling point. Everyone else likes the other points and don't realize the actual selling point of the language.
>Rust is that for C++

No it's not. Rust has roots in functional languages. It is completely orthoganol to C++.

Graydon Hoare, a former C++ programmer on Mozilla Firefox and the original creator of Rust, acknowledges that for many people, Rust has become a viable alternative to C++ :

https://graydon2.dreamwidth.org/307291.html

And on slide #4, he mentions that "C++ is well past expiration date" :

https://venge.net/graydon/talks/intro-talk-2.pdf

It's possible that Graydon's earliest private versions of Rust the 4 years prior to that pdf were an OCaml-inspired language but it's clear that once the team of C++ programmers at Mozilla started adding their influences, they wanted it to be a cleaner version of C++. That's also how the rest of the industry views it.

> Rust has become a viable alternative to C++

Alternative yes, derivative no. Rust doesn't approach C++'s metaprogramming features, and it probably shouldn't given how it seems to be used. It's slightly self-serving for browser devs to claim Rust solves all relevant problems in their domain and therefore eclipses C++, but to me in the scientific and financial space it's a better C, making tradeoffs I don't see as particularly relevant.

I say this as a past contributor to the Rust std lib.

So?

Zig, D, and C are also alternatives to C++. It’s a class of languages that have zero cost abstractions.

Rust is NOT a beautiful language hiding inside of C++. It is not an evolution of C++. I’m pointing out that what you said is objectively wrong.

Can rust replace C++ as a programming language that has a fast performance profile due to zero cost abstractions? Yes. In the same way that Haskell can replace Python, yes it can.

> Rust and C++'s biggest mistake, not passing allocators into containers and functions

Rather, basing its entire personality around this philosophy is Zig's biggest mistake. If you want to pass around allocators in C++ or Rust, you can just go ahead and do that. But the reason people don't isn't because it's impossible in those languages, it's because the overwhelming majority of the time it's a lot of ceremony for no benefit.

Like, surely people see that in C itself there's nothing stopping anyone from passing around allocators, and yet almost nobody ever does. Ever wonder why that is?