4 ms·
This is a really great writeup! I was using Rust as my main programming language from some months before 1.0 up until maybe early 2019. I have only written some
by martinhath 6y ago
This is a really great writeup! I was using Rust as my main programming language from some months before 1.0
up until maybe early 2019. I have only written somewhere in between 100 and 1k lines of Zig, but
generally feel that I agree with most of what's being brought up here.
Here's a mind dump:
> Zig manages to provide many of the same features with a single mechanism - compile-time execution of regular zig code. This comes will all kinds of pros and cons, but one large and important pro is that I already know how to write regular code so it's easy for me to just write down the thing that I want to happen.
This is a huge one for me, and I really don't understand why Rust didn't jump on this earlier.
Using the programming language for configs, generics, macros, and anything else that you'd want
at compile time just seems like such a huge win, instead of having weird preprocessors-like systems,
some config file format with arbitrary limitations, and weird marcro-like systems that either
have crazy syntax (like `macro_rules!` in Rust), or that are way too limiting (like `const` functions in Rust).
Jon Blows language seem to take a similar stance as Zig; let's see if it ever hits public beta.
> On the other hand, we can't type-check zig libraries which contain generics. We can only type-check specific uses of those libraries.
This is definitely a concern I have about this "C++-like generics".
My experience with C++ suggests that this is bad, but on the other hand, improving even just error messages
would be such a day-night improvement, that I don't really trust my judgement on this one.
> Both languages will insert implicit casts between primitive types whenever it is safe to do so, and require explicit casts otherwise.
Is this really true? I seem to recall having to have plenty of `as usize` in my code when
using smaller integer types for indices, but maybe this has changed (or maybe I'm misreading what's being said here).
> In rust the Send/Sync traits flag types which are safe to move/share across threads. In the absence of unsafe code it should be impossible to cause data races.
This is probably Rust's main selling point, because as far as I can tell, no other mainstream language comes even
close to getting static thread safe guarantees (up to your definition of mainstream).
As time goes on, however, I'm getting less and less excited about this, because most of my programs
are not multithreaded, and the very few times that I need multiple threads, there is often
very obvious and small boundaries in between the threads.
It's just not very interesting to me that I _could_ be writing programs with thousands of threads
all jumping around without having to worry about data races, because I don't really worry about it
in the first place. But still, I as a Rust programmer, have to pay the price for this option being available.
> Undefined behavior in rust is defined here. It's worth noting that breaking the aliasing rules in unsafe rust can cause undefined behavior but these rules are not yet well-defined.
I'm not sure what to say about this, except that it's surprising that there seem to be a
lack of voices about this in the Rust community.
How can anyone comfortably write `unsafe` code without knowing what the rules are?
Especially when the compiler is so "good" at depending on the "rules"?
I don't understand.
I have pretty limited experience with unsafe, but I have written some, and
was often confused about which bugs were my logic bugs and which were the compiler assuming
I didn't break some rule I didn't know about.
Combine this with a poor debugging story overall, and you have a pretty miserable experience programming.
Maybe this isn't a problem in practice, or maybe all people succesfully writing `unsafe` code for libraries
are also `rustc` veterans?
> @import takes a path to a file and turns the whole file into a struct. So modules are just structs.
This is a very nice approach! I remember from earlier Rust that the module system was a real pain point
for beginners, and can also remember really struggeling with it.
Curiously though, I also remember looking back, not understanding why anything was confusing about it.
This was also redone(?) at some point, and I think it's nicer now.
> In rust my code is littered with use Expr::* and I'm careful to avoid name collisions between different enums that I might want to import in the same functions. In zig I just use anonymous literals everywhere and don't worry about it.
I've always been bothered by Rust's inability to infer the `enum` type in a `match`;
in other places Rust has no problems being automagick, and this is really very annoying to go around,
either with `use Foo::*` before each match, or having it in file scope and hope for no collisions.
Zig seems to take exactly the approach I'd go for.
> Re allocators
I think Zig's stand on explicit allocators is very good; I've seen enough bad code in other languages
that allocates here and there for things that, very clearly, doesn't need to be there.
Having the language be explicit about allocations makes it easier to stop and say "hey wait a minute, is
this realy the way I'm supposed to do it?", but without having to jump through hoops
if you _just_ want to allocate something somewhere (define a global allocator yourself).
And, as a bonus, it's easier to handle the allocations of other peoples code.
> Zig has no syntax for closures.
I definitely need to write more Zig to find out whether this is a problem or not.
I've written a lot of C++ lately, and while there _are_ closures available, I think I've only used them once.
Maybe the reason for my comparatively heavy closure usage in Rust was that so many methods in the standard libray
took closures that you're shephearded into making similar methods for your own types.
> Zig's error handling model is similar to rust's, but it's errors are an open union type rather than a regular union type like rust's.
I really think the error story is why I prefer Zig to Rust now.
The giant error `enum` in Rust is definitely what I'd go with because it's simply not feasible to
manually track which functions return what errors and making individual enums yourself,
even though this is super easy for the compiler to do, like Zig shows.
Not to pick on anyone in particular, but sometimes it feels like many programmers think
that a program only consist of the happy path and that errors are somehow rare and not
worth dealing with properly. Both Rust and Zig are huge helps to combat this mindset, but I do think
that Zig comes out ahead, simply by being less annoying to work with.
Also, while some people might say that `Result` just being a part of `core` and not a magic special language thing
is cool, I do appreciate Zig's usage of `?` since it's way less typing for something that happens _all_ the time.
> Zig's compilation is lazy. Only code which is actually reachable needs to typecheck. So if you run zig test --test-filter the_one_test_i_care_about_right_now then only the code used for that test needs to typecheck.
I didn't know this, but this is awesome!
> Zig has absurdly good support for cross-compiling.
I've never understood why cross-compiling isn't an out-of-the-box feature in all languages.
Don't you basically just have to target a different instruction set?
Well, and a different executable format.
But still, compared to all of the other crazy things compilers are doing,
this seems very straight forward in comparison.
> Zig has an experimental build system where the build graph is assembled by zig code.
See above. I really really really don't understand why all languages doesn't do this already.
> In rust, blocks are expressions.
This is something I really like about Rust and a pattern I've used a lot, where I'd say
let some_thing = {
let foo = ...
let bar = foo.baz() + quiz();
...
foo
};
to avoid accidently using `bar` somewhere else. Granted, since Rust allows shadowing this isn't really
a problem most of the time, but it's definitely something I miss when writing C++.
Zigs version is somewhat verbose, but I'll manage.
> There is an in-progress incremental debug compiler for zig that aims for sub-second compile times for large projects. Based on progress so far, this is a plausible goal.
Andrew's work on binary patching executables is really cool.
I hope we'll get to compile times this low, even for moderately sized projects.
> real 23m27.475s
This is just sad. Despite all the work the contributors to `rustc` are doing, it just seems that
they are in a completely different league with respect to compile times than what I'd like.
I hope the steady progess they're making will either make some jumps, or continue for a while :)
> Main points so far:
This is a great summary, and I think people reading it (or the whole post) will have a pretty good idea of
where they stand re. the two languages.
- steveklabnik 6y ago(By the way, Hacker News doesn't support markdown; indent your code by two spaces, rather than using fences. https://news.ycombinator.com/formatdoc https://news.ycombinator.com/formatdoc ) Just a few small things: > This is a huge one for me, and I really don't understand why Rust didn't jump on this earlier. Doing this well is not easy. Exposing a full language at compile time isn't a difficult feature, but doing compile-time execution in a sound, safe way is not simple. For example, cross compilation becomes more of a thing. It is easy to accidentally break the type system. I don't actually know how Zig implements comptime, but I expect given Andrew's chops, it's probably in a good way. (And, one can argue that there are pros and cons here too, the article gets into them a bit. Doing everything this way has significant drawbacks, as well as significant pros.) > I'm not sure what to say about this, except that it's surprising that there seem to be a lack of voices about this in the Rust community. How can anyone comfortably write `unsafe` code without knowing what the rules are? Especially when the compiler is so "good" at depending on the "rules"? I don't understand. I have pretty limited experience with unsafe, but I have written some, and was often confused about which bugs were my logic bugs and which were the compiler assuming I didn't break some rule I didn't know about. Combine this with a poor debugging story overall, and you have a pretty miserable experience programming. There's just not a lot to say. The exact details are being worked on. This takes time. The team isn't going to make rules that invalidate large swaths of existing code, and has a history of making compatibility warnings with a long time before things break. There is a fairly clear list of "this is absolutely not acceptable," and a bunch of "it depends...". It's also just that the vast majority of people never need to reach for unsafe in the first place, so it isn't a huge pressure on a lot of people.
- mtzet 6y ago> Doing this well is not easy Does this also apply to the in-language build system? Given both Rust and Zig's ergonomics, it just seems so brilliantly simple (at least in hindsight) to let the build system be a library. Is it just coincidence that I've only heard of this approach for zig and Jonathan Blow's language, or is there a technical reason this is more difficult than it seems?
- 6y ago