Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

What are the pros and cons of Zig vs Rust? I see Zig mentioned more and more here, especially with regards to threads about Bun, the largest / most popular project in Zig currently, but I'm not sure why I should use it over Rust. Zig seems like it still does not solve the problem of memory safety, in that it has safety issues that would be caught by a borrow checker or other such memory safety checker [0]. You can still use after free, for example:

    var hello = try allocator.dupe(u8, "hello world");
    allocator.free(hello);
    std.debug.print("{s}\n", .{hello}); 
[0] https://www.scattered-thoughts.net/writing/how-safe-is-zig/


In terms of safety, Zig is somewhere in the middle between C and C++ on one side, and Rust on the other side (e.g. it enforces much more correctness than C or C++, and has runtime spatial, but not temporal memory safety (but it has a debug allocator which doesn't recycle memory (or rather: address ranges) and which should catch most temporal memory safety problems - like your example).

Zig doesn't have separate sublanguages for macros and generics, instead these are all handled with regular 'comptime' Zig (along with type reflection).

Zig has builtin syntax sugar for dealing with optionals and error unions, which makes code less noisy (although that may be subjective - and I think Rust also got some of that over time).

Zig's interaction with the C world is excellent, and to a lesser degree also to the C++ and ObjC world (the Zig compiler can compile C++ and ObjC code, but still requires a C shim to sit between Zig and C++ or ObjC code).

Zig has much less influence from high level functional languages and less "type system wankery" compared to Rust, this again is a good thing for some people, while disliked by others.

A minor detail, but which I think demonstrates the Zig philosophy: the Zig compiler executable is also the build system and package manager. In Rust this is delegated to a separate tool (cargo).

...and my personal opinion: Zig is a lot more 'elegant' than Rust which results in more joy when writing Zig code. Rust feels entirely too much like 'designed by committee'. With Zig I "grokked" the language in a weekend of tinkering, while with Rust I never really had "fun" despite several attempts of trying to like it.

TL;DR: If I had to create an absolutely waterproof and critical isolated piece of code, I would probably write that in a "simple Rust subset". For anything else I would definitely prefer Zig.


The C/C++ interop in Zig gives it a great value proposition for embedded systems programming. My experience of that culture has been that C++ is "too unpredictable", so C is the language of choice. And glob help you if you want to use dynamic memory allocation. Zig could replace C and the preprocessor and support programming at a higher level of abstraction (generics).

I've tried a couple of times now to get my head around Rust with small efforts. Apparently I need big efforts and deep immersion. "A half-hour to learn Rust"[1] certainly helped with understanding the syntax around lifetimes. I keep reading that understanding the borrow-checker and lifetimes should be a snap for C/C++ programmers. Well, I've been doing C++ for over 30 years and C for longer, and Rust just strikes me as a bondage and discipline language.

[1] https://fasterthanli.me/articles/a-half-hour-to-learn-rust


Insightful, thanks! I was wondering about the pros and cons of Zig vs Rust, and your comment shines light on various angles. It confirms how I unconsciously felt about Zig, that it's a well-designed language which succeeds in reducing the complexity of concepts to learn, and achieves a good balance of simplicity, fun, power, and cleverness.


Is memory safety the only thing holding you back? Suppose there were a way to do static borrow checking, as a 3rd party tool?


You shouldn't use it over rust... that's a nonsensical question. That's like asking when you should use a hammer instead of a screwdriver.

I use zig because it's enjoyable to use. I hate writing rust, it feels like the language is actively fighting me. I started writing zig, and started loving the process of simplify writing code again. Zig is nice because, to steal their quote. I'm able to spend my time debugging my code, and not debugging my understanding of the language. I've stepped on a use after free but because it's so easy to write and use tests, I found it and fixed it in tests.

Bugs are undesirable, but I can write just as many bugs in rust. So I'm always so confused about why rust users are so panicked about use after free, or other memory bugs. Like somehow the only bugs rust has a handle on is the worst class of bug? Such an odd idea I can't figure out...


They typical argument given for the focus on memory related bugs is that:

β€˜{company,Github,bug/cve -tracker} says (following code review | testing)* that 70% of reported bugs/exploits are due to memory management failures.’

I write it thusly, because I don’t actual know or remember all the various permutations I’ve seen. But a few that I can recall being mentioned would be Microsoft and Google, regarding Windows and Chrome (or it could have been the entire Google production code base). But other than these types of statements I can’t recall any support for focusing on memory vs other error kinds. This line of argument has led to developers who class memory β€˜unsafe’ languages as objective moral bads (and the development of such to be inherently morally bad) [I was literally told this on HN, in the furor over the announcement of Hare Lang]. I think it’s reactions like this that lead to your usage of the word panicked, but I always try to assume the majority of developers are just using a language they enjoy and getting on with the business of making stuff.

Just as a parting note: I’m not attempting to disagree with the statements by these companies, or with the general premise that memory safety errors result in problems for users of software. I’m slightly ambivalent to the whole thing.

β€” edit for spelling


I think your wording is off, because 70% of reports bugs are not memory related.

I vaguely recall that 70% of *certain classes* of reported security flaws were buffer misuses (which zig handles quite nicely in releasesafe and using slices, which is recommended)

Edit:

Found it:

70% of security bugs were counted as safe buffer related. Note that by sticking to slices and releaseSafe, zig provides most of the same guarantees as rust in this arena.

It’s unclear whether a buffer issue automatically classified a bug as a security flaw as well, as in many cases it’s simply not. For example, a memory security flaw was reported in Stockfish that was simply not a flaw.

We are also discussing the Microsoft OS, which is a whole different beast than applications.

Overall, meh.


"70 percent of all security bugs are memory safety issues" (https://www.zdnet.com/article/microsoft-70-percent-of-all-se...)


I probably should have substituted 70% with (some percentage big enough to attempt to draw conclusions). But I think I got the 70% from so β€˜blank says 70% of exploits in our codebase are memory related’, but I could be mis-remembering. I tried to make it as broad and encompassing as I have seen the claim used in arguments for memory-safety-centric programming.


About 20 percent of all bugs in the Linux kernel are use-after-free.

With that said, I think proper tooling can solve much of the problems with unsafe memory languages. Question is then, why doesn't it happen? Are developers uninformed, arrogant or just lazy? Is forcing it into the language the only way to solve this?


dunno about x% of all exploits but this is a systematic attempt at count/census of 6 categories memory safety failures

https://www.scattered-thoughts.net/writing/assorted-thoughts...


Because I can either catch those bugs myself or let the computer catch them for me. I'm lazy and a fan of letting the computers do the work at compile-time so I don't have to run into them at runtime, at 3 AM in production. It's not the amount of bugs, I can also write out of bounds panic bugs, it's the type of bugs.

And truth be told, if we get liquid types or dependent types in Rust, even those runtime out of bounds checks would be caught at compile-time too.


This argument doesn't make sense to me either, but maybe I'm just stupid.

For something to die at 3am in production, that means either someone pushed code way too late, or something changed in the stack unpredictability. Your compiler wouldn't have been able to predict either of those cases. But bugs are bugs, if I'm called at 3am to fix a bug, I don't want to spend hours trying to fight my compiler, and then fall back asleep while I'm waiting for it to compile. I want to quickly identify the issue, and then to be able to write the smallest possible patch until morning. In my limited experience, rust is conducive to neither of those options.

And you said "if we get liquid types" I assume that means you meant it to say if rust gets? Unless you meant to identify as part of the rust language?


My point is that the more bugs that are caught at compile-time rather than runtime, the better, as it's a much lower likelihood that something randomly breaks in the middle of the night. It has nothing to do with when someone pushed their code out or the stack changing. Someone could have pushed a runtime bug that was not caught (but that could have been caught at compile-time, depending on the type of bug, of course) that then breaks production later on when a specific thing gets triggered. You wouldn't be fighting the compiler at 3 am as well as the code because you likely wouldn't be fixing things at 3 am in the first place, and if it's not something that's caught at compile time, well then you'd be dealing with the language either way.

Compile-time checks help you write better code, generally speaking.


The idea that β€œcompile time checks help you write better code is unanimous and absolute” is simply false.

Rust itself demonstrates that compile time checks sometimes result in worse code, as many people have found especially when they need to do things rust doesn’t like for performance reasons.

For sure, some checks are unambiguously good. But there is also definitely a moving target of a line towards whether or not more checks has diminishing returns if not negative returns on your code. That moving line will depend on a lot of things.

You also need to be careful because β€œmore checks” doesn’t mean β€œless work” as you’ve asserted. It means more work in a lot of safe pointer scenarios in rust, and more work actually does often translate to more logic bugs and more complexity when anything changes.

As far as I can tell, more checks doesn’t same any time, it often just changes where your time is spent.


Not a Rust developer, in what way is Rust forcing you to write less performant code? Any good example?


The borrow checker is really at its happiest when your ownership and borrow structure is tree-like, so uni-directional references and ownership, because it's much easier to analyse.

If the ownership structure that gives the best performance for your problem is not tree-like, you will run into issues with the borrow checker. There are ways to tackle it: you could keep the non-tree-like structure and use reference counting and RefCells to move the borrow checking to runtime, or its possible you could use a less performant but tree-like structure that the borrow checker accepts, but both of these examples would come at a performance cost.

It's kinda hard to be more specific because it's heavily dependent on the exact problem.


I doubt you'll have anyone take you up on this offer. Goal posts for "good example" get moved every time anyone tries. Anything using Arc is a good place to start though.


> For something to die at 3am in production, that means either someone pushed code way too late, or something changed in the stack unpredictability.

I don't think those are the only two scenarios. I think the most likely scenario by far is user input triggering a latent issue in a code path and promptly bringing the application down. A classic example, of course, is a missing bounds check.

Modern language design makes this less likely by enforcing invariants, adding checks, and making these kinds of issues impossible by construction. It doesn't mean that there isn't a space for languages without these safe guards, but it does mean you should have a really good reason to use them.

> But bugs are bugs, if I'm called at 3am to fix a bug, I don't want to spend hours trying to fight my compiler, and then fall back asleep while I'm waiting for it to compile. I want to quickly identify the issue, and then to be able to write the smallest possible patch until morning. In my limited experience, rust is conducive to neither of those options.

In my experience with Rust specifically, I've actually found it easier to make those kinds of fixes as the area of code I have to reason about is much smaller. I don't have to worry that a local change will have global effects, as I can much more clearly see the dataflow. It's not a silver bullet, but I've felt much more comfortable doing emergency fixes on Rust than on C++.


Zig is trying to be a new C. Rust is trying to be a low-level ML. Sometimes people say that "Rust is trying to be the new C++" but this is only true in a more abstract sense that looks at use cases β€” it doesn't follow the same technical path C++ did.

You could probably make a reasonable analogy that Zig:Rust::Go:OCaml but this isn't perfect. Zig is simpler to work with but it doesn't have as many powerful features, like certain Rust safety guarantees, as you mentioned.


>doesn’t have as many powerful features

comptime is easily one of the most powerful language features of any language in existence. Comptime gives you literally everything you get from macros and generics without the complexity of needing to know 3 separate Turing complete languages to get it.

Zig doesn’t need a horde of random highly complex differently rule bounded features all complexily interacting with one another because it has 1 easy to use feature that eliminates those needs.


> Comptime gives you literally everything you get from macros and generics without the complexity of needing to know 3 separate Turing complete languages to get it.

comptime doesn't allow you to create domain specific macros like html! in Rust, it also doesn't allow you to constrain what types are accepted by your functions. It also has its own problems with long compile times.


> it also doesn't allow you to constrain what types are accepted by your functions.

It does, if by constrain you mean β€œsucceed or fail”: just write a comptime if statement and @compileError. If you mean a mechanism like concepts or SFINAE then you are correct.


> comptime doesn't allow you to create domain specific macros

Sure it does!

Zig's string format function is written using comptime. It parses the format string and validates the arguments at compile time. See https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig

I also toyed around with turning a math equation dsl into compiled statements at https://github.com/Laremere/alg I got matrix math working so, eg, if you multiply a 2by1 matrix by a 1by2 matrix, it returns a matrix which is typed to 2x2.


The concept of β€œinterfaces” in zig is a comptime pattern rather than a language construct. See std.io.SeekableStream.

To check that an anytype meets the decls of SeekableStream is rudimentary.

It’s funny that you say that about html, as a project in the discord was just released to get comptime html only in the last few weeks. I don’t remember the project name unfortunately.


Ah, if only Rust didn't drive away the person who was working on the comptime equivalent in Rust. Sadly, now it is likely that that will be a long way off.


If only 1% people whining about comptimes actually sat down and looked into adding it, we'd have comptimes already.

Disclaimer: I'm tentatively exploring this via macros.


> Rust is trying to be a low-level ML

Yep, this is probably why I like it so much. I liked OCaml when I used it and Rust derives a lot of features from OCaml, as well as its initial compiler being written in OCaml, so I think the creators saw how well parts of OCaml fit together (Option and Result types, ADTs, etc) and added it to Rust, as Rust was much different than currently [0].

[0] http://venge.net/graydon/talks/intro-talk-2.pdf


For me, Zig is more of a better C plus. If I want memory safety and high-integrity software at this point, I use SPARK2014, a subset of Ada. It has a longer legacy for mission-critical software than Rust. Adacore, a private company that provides support for Ada and SPARK, are collaborating with Ferrous Systems to bring all the goodness to Rust, but Rust does not have the legacy applications or all the goodies (yet).


Rust is β€œa better C++” (for lack of a cleaner allegory).

Zig is β€œa better C”.

Understanding that makes it clear where Zig shines and Rust is less ergonomic.

Both are good, but I see them as complementary and not really adversarial or in competition.


Rust is what most *should* use for system-level programming and/or overall programming, if possible.

Rust is safer, better overall + ergonomics, ecosystem, etc.

However, Zig have some nice properties:

- Is easier to do bit/memory wrangling in Zig than in Rust

- Is FAR easier to cross-compile. To the point that for example I use Zig to compile my Rust project for musl

- Is MUCH faster to compile

- Is far "smaller" that is nice in constrained situation (not just "environments", for example, you can use zig for when you will use gcc/llvm to compile some small c file)

Zig is a very good match if the scope of the project is relatively small. Rust shine much more when is more big or you need to work for a team of people.

Also, if you plan to make a library that will be used by others, better to use a safer lang.

I don't see both as competitors. They are excellent complements, and my dream is to Zig to replace C and Rust/ada/pascal to replace C/C++.

P.D: A probably good match is to make some low-level thing in Zig (where before was C) and FFI it in Rust.


β€œWhy Zig When There is Already C++, D, and Rust?”

https://ziglang.org/learn/why_zig_rust_d_cpp/


Zig is not supposed to be a Rust equivalent. It's supposed to be C but with the problems fixed.

https://youtu.be/Gv2I7qTux7g




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: