Is it worth learning Rust or Zig and dealing with the borrow checker or manual memory management in general, or are GC languages like Nim or Crystal good enough? I'm not doing any embedded programming by the way, just interested in command line apps and their speed as compared to, say, TypeScript, which is what I usually write command line apps in.
Obviously i'm biased, but i quite enjoy it. I find i am more efficient now than before, because it manages to give me the ease of the "easier" languages quite often with a rich set of tooling when i need to go deeper.
Personally i feel the concern over the borrow checker is way overblown. 95% of the time the borrow checker is very simple. The common worst case i experience is "oh, i'm borrowing all of `foo` when i really want `foo.bar` - which is quite easily solved by borrowing just the `.bar` in a higher scope.
The lifetime jazz is rarely even worth using when compared to "easier" languages. Throw a reference count around the data and you get similar behavior to that of them and you never had to worry about a single lifetime. Same goes for clones/etc.
I often advocate this. For beginners to use the language like it was a GC'd language. A clone or reference count is often not a big deal and can significantly simplify your life. Then, when you're deeper into the language you can test the waters of lifetimes.
So yea. Your mileage will vary i'm sure. But i find Rust to be closer to GC'd languages than actual manual languages, in UX at least. You won't screw up and leak or introduce undefined behavior, which is quite a big UX concern imo.
- The borrow checker is one of the easier parts of Rust to grok, it's just as you say, not that complicated in the end.
- Traits are more annoying to understand and find in source code when they can get added from anywhere, and suddenly you code gets extra functionality, or it's missing the right one unless you import the right crate but there is no "back-reference" so you're not clear what crate the code actually comes from.
- Crates/libraries are harder to grok with their mod.rs/lib.rs files and whatnot, in order to structure your application over many files.
- Macros are truly horrible in Rust, both to write and debug, but then my only experience with macros are with Clojure, where writing macros is basically just writing normal code and works basically the same way
- Compilation times when you start using it for larger projects tend to be kind of awful. Some crates makes this even worse (like tauri, bevy) and you need to be careful on what you add as some can have a dramatic impact on compilation speed
- The async ecosystem is not as mature as it seems on first look. I'm really looking forward to it all stabilizing in the future. Some libraries support only sync code, others only async but only via tokio, others using other async libraries. Read somewhere that eventually it'll be possible to interop between them, time will tell.
- Control flow with Option/Result and everything that comes with it is surprisingly nice and something I'm replicating when using other languages (mainly a Clojure developer by day) now.
My development journey was PHP -> JavaScript -> Ruby -> Golang -> Clojure with doing them all the capacity of backend/frontend/infrastructure/everything in-between, freelancing/consulting/working full-time at long term companies, so take my perspective with that in mind. Rust would be my first "low-level" language that I've used in larger capacity.
I have been using Rust professionally as well and had a different experience. For anything singlethreaded I agree with you. For any asynchronous behavior, whether it's threads or async or callbacks, the borrow checker gets very stubborn. As a response, people just Arc-Mutex everything, at which point you might as well have used a GC language and saved yourself the headache.
However, enums with pattern matching and the trait system is still unbeatable and I miss it in other languages.
There's very little allocation since it supports returning VLAs (like strings) from functions via a secondary stack. Its Alire tool does the toolchain install and provides package management, so trying the language out is super easy. I've done a few bindings to things in C with it, which is ridiculously easy.
> Is it worth learning
Languages are easy to pick up once you understand fundamentals. The borrow checker is intuitive if you have an understanding of stack frames, heap/data segment, references, moved types, shared memory.
You then should be asking "Is it worth using?", then evaluate use cases.. pros/cons.. etc.
For CLI, Rust is likely the easiest given it's macros, but if you struggle with the borrow checker then it won't be. You will be fighting the compiler instead of developing something.
Depending what your CLI program is doing, you might want to evaluate what libraries are available, how they handle I/O, and parallelism.
JavaScript has incredibly easy and fast concurrent I/O thanks to libuv and v8.
> Languages are easy to pick up once you understand fundamentals. The borrow checker is intuitive if you have an understanding of stack frames, heap/data segment, references, moved types, shared memory.
I see this sentiment often. In the last 10 years, I have come up a level in raw C, learned Kotlin, Swift, Python, Elixir/Erlang, and a smattering of JavaScript, all coming from a background that included Fortran and Smalltalk.
My problem with the dialogue is what is meant by “learn.” I have architected, implemented, and maintain different components of our products in all these languages currently. I think that demonstrates I have “learned” these languages, at least at this level of “picked up.” But I can’t write Python the way Brett Canon does. Or Elixir the way Jose Valium does. Or any of their peers. And in that regard I still very much feel I have not “learned”.
I spent a couple days playing with Zig a month or so ago. I became familiar with the way it worked. I could spend another month or so in that phase, and then could probably comfortably accomplish things with it. But I don’t think I’d feel like I’d “learned” it.
It reminds me of my experience learning Norwegian. I lived in Norway for 2 years and did my best to speak as much Norwegian as I could. At six months I could definitely get by. At 13 months, as I embraced the northern dialect, I was beginning to surprise Norwegians that I was from the states. I started dreaming in Norwegian at some point after that. But even at 24 months, able to carry on a fluid conversation, I realized I still could “learn” the language better than I currently knew it.
So I guess, it always seems there needs to be more context, from both the asker and the answerer, when this “should I learn X” discussion is had. Learning is not a merit badge.
For a language that is around 8 years old, this may be a serious problem, since the surrounding ecosystem has been probably written without parallelism in mind, and it may take a very long time to be updated (if ever).
The language team has done a great job rounding rough edges, and this next roadmap is slated for even more polishing. They heavily prioritize dev experience which is why i think people like myself (a GC'd language person historically) use and love Rust so much.
Any programming language is good enough for their own use cases :) It's a matter of understanding which the use cases are.
I'm a big Rust fan, but I nonetheless believe that the use cases for programming languages with manual memory management are comparatively small, in particular, since GC has been improved a lot in the last decade.
For undecided people, I conventionally suggest Golang. Those who at some point need deep control, will recognize it and change accordingly.
My question could be further constrained then to be, is learning Rust or Zig despite its manual memory management worth it for applications that are normally already garbage collected in their current implementations? Or are languages like Nim and Crystal enough? Does Rust and Zig have other benefits despite manual memory management?
Nim also can double as a web language by transpiling to JS.
Then you can automate code generation with the sublime macros, which are just standard Nim code to create Nim code. No new syntax or special rules required - any Nim code can be run at compile time or run time, so you can use standard/3rd party libraries at compile time to write macros and give the user a slick syntax whilst removing boilerplate.
I really miss languages without straight forward metaprogramming after using Nim. It's something that multiplies the power of a language, rather than just adds to it.
There are no fast languages, only fast language implementations.
NodeJS/V8 is pretty fast (even faster than you probably think)—particularly if you're already doing things like making the sort of compromises where you limit yourself to writing only programs that can be expressed under the TypeScript regime. It's usually the case that it's not the NodeJS runtime that is the problem but rather the NodeJS programming style that is the source of the discomfort with "speed" that you will have experienced.
The Zig self-hosted compiler codebase consists of 197,549 lines of code.
There are several different backends, each at varying levels of completion. Here is how many behavior tests are passing:
LLVM: 1101/1138 (97%)
WASM: 919/1138 (81%)
C: 740/1138 (65%)
x86_64: 725/1138 (64%)
arm: 490/1138 (43%)
aarch64: 411/1138 (36%)
As you might guess, the one that this milestone is celebrating is the LLVM backend, which is now able to compile the compiler itself, despite 3% of the behavior tests not yet passing.The new compiler codebase, which is written in Zig instead of C++, uses significantly less memory, and represents a modest performance improvement. There are 5 more upcoming compiler milestones which will have a more significant impact on compilation speed. I talked about this in detail last weekend at the Zig meetup in Milan, Italy[1].
There are 3 main things needed before we can ship this new compiler to everyone.
1. bug fixes
2. improved compile errors
3. implement the remaining runtime safety checks
If you're looking forward to giving it a spin, subscribe to [this issue](https://github.com/ziglang/zig/issues/89) to be notified when it is landed in master branch.
Edit: The talk recording about upcoming compiler milestones is uploaded now [1]
Does using Zig over C++ lead to "less memory, and represents a modest performance"? Or was the C++ implementation a bit sloppy? (lacking data oriented design for instance)
Also, what specifically are you most excited using Zig for?
The new Zig implementation is certainly more well designed than the C++ implementation, for several reasons:
* It's the second implementation of the language
* It did not have to survive as much evolution and language churn
* I leveled up as a programmer over the last 7 years
* The Zig language safety and debugging features make it possible to do things I would never dream of attempting in C++ for fear of footguns. A lot of the data-oriented design stuff, for example, makes use of untagged unions, which are nightmarish to debug in C++ but trivial in Zig. In C++, accessing the wrong union field means you later end up trying to figure out where your memory became corrupted; in Zig it immediately crashes with a stack trace. This is just one example.
* Zig makes certain data structures comfortable and ergonomic such as MultiArrayList. In C++ it's too hard, you end up making performance compromises to keep your sanity.
Generally, I would say that C++ and Zig are in the same performance ballbark, but my (obviously biased) position is that the Zig language guides you away from bad habits where as C++ encourages them (such as smart pointers and reference counting).
As for less memory, I think this is simply a clear win for Zig. No other modern languages compete with the tiny memory footprints of Zig software.
Some of the projects I am exited to use Zig for:
* rewriting Groove Basin (a music player server) in zig and adding more features
* a local multiplayer arcade game that runs bare metal on a raspberry pi
* a Digital Audio Workstation
That's particularly interesting considering the rust compiler in rust has never been as fast as the original OCaml one
I was there and had to suffer through this more than virtually anyone else :)
The reduced memory has significant value. Being able to do the same build on less expensive hardware or do more with the same hardware is a significant financial performance improvement
* Is that a list of compilation targets?
* If not all behavior tests pass, does that not mean that the compiler fails to compile programs correctly?
Please indulge those of us who are not familiar with self-hosting compiler engineering.
Snippets of zig code that use language features and then make sure those features did the right thing. You can find them here: https://github.com/ziglang/zig/tree/master/test/behavior
> Is that a list of compilation targets?
Mostly. Pedantically, it's a list of code generation backends, each of which may have multiple compilation targets. So for example the LLVM backend can target many architectures. The ones that are architecture specific are currently debug-only and cannot do optimization.
> If not all behavior tests pass, does that not mean that the compiler fails to compile programs correctly?
Some tests are not passing because they cause an incorrect compile error, others compile but have incorrect behavior (miscompilation). Don't use Zig in production yet ;)
(edit: fix formatting)
It's a pretty common state of affairs, actually. Often arises out of the second or third implementation of the compiler being much better than the first attempts, probably coupled with the momentum of people using the language who can contribute to the tooling because it's in the same language.
https://elephly.net/posts/2017-01-09-bootstrapping-haskell-p...
I also wish we could rewrite everything in a modern language, but the reality is that we can’t and that if we could, it would take a LONG time. The ability to start new projects, or extend existing ones, with a modern and more ergonomic language—Zig—and be able to seamlessly work with C is incredible.
I look forward to the self-hosted compiler being complete, and hopefully a package manager in the future. I’d really like to start using Zig for more projects.
What zig offers is even better - because zig included a CC, you can actually reduce complexity with zig by getting a single compiler version for all platforms, rather than a fixed zig + each platform’s cc. And with it, trivially easy cross-platform builds - even with existing C code. That’s cool! Go has excellent cross-compilation, but Go with C, not so much.
Rust is a powerful tool, but it’s a complex ecosystem unto itself - both conceptually and practically. It has great interoperability frameworks, but the whole set of systems comes at a substantial learning cost. Plus, porting an existing software design to Rust can be a challenge. It’s more like the “scala of C” if we’re trying to stretch the analogy past the breaking point.
On the JVM world not really.
That's the Maintain it With Zig approach :^)
Fast compilation speeds are a nice to have but basically irrelevant for normal developing of software in my experience.
Your day job project takes half an hour to compile and you don't see the point in speeding that up?
i.e how can someone look at a self hosted zig compiler and build it themself from source, never needing to download blobs from the internet?
Otherwise you lose the ability to trust anything you build.
Unless you’re recording memory contents and executing instructions by hand, you’ve just discovered the Ken Thompson hack. At some point the pragmatic thing is to trust some bits from a trusted source (e.g. downloaded from an official repo w/ a known cert, etc.).
C17 -> assembly of your choice, written in assembly
Zig -> C17 “transpiler”, written in C17
I’m assuming these will primarily be for super fast debug builds (at least to start), and LLVM (and maybe C backend too) will still be the favored backend(s) for release builds.
Tons of compilers emit machine code.
How suitable is zig for such a task compared to say rust?
I know that higher level languages will have primitives that aim to represent strings, but if you need to get into the weeds with Unicode then you'll be leaning on a library regardless
Yes would agree and see them as platform related. It's just too large a task to create from scratch. Like say on JVM you can compile to bytecode and have strings already built into platform, and java.time, and ability to access an ecosystem of libraries.
With zig could one could use c or rust libraries?
Just parsing C++ is a rather large undertaking (my understanding is that it's syntax is turing complete).
Tracking issue for overall progress on the self-hosted compiler: https://github.com/ziglang/zig/issues/89
Zig's New Relationship with LLVM: https://kristoff.it/blog/zig-new-relationship-llvm/
edit: all your base are belong to us