Rust Programming
On the garbage collector point: I used to do java professionally, and it made me never want to use any garbage collected language ever again.
I have some nightmare stories... one giant monolithic program was so bad on memory, that after running for a few days, it would take up all the memory of a 24 GB machine, and one of our devs had to manually write a ton of code to garbage collect certain things just to keep the thing alive.
Even small programs in GC languages have that problem: they can never really be smart enough to know which data can be scrapped, and do the safest thing, which is to keep it all in memory. Even a simple java program I wrote a few years ago grows to fill up the memory of the 1gb box I have it on, after it runs for a few weeks.
Rust doesn't have that problem, as it forces you to think about borrowing, stack and heap from the very beginning. When you realize its not that much more work to do so, you can see why go and java will never beat rust on memory performance.
Rust doesn't have that problem
That's a bit generalized you can leak memory in rust e.g. by keeping around old Weak pointers, also std::collections::* don't shrink by themselves.
I feel like Rust isn't good for anything that has unsafe in it, or anything that needs to call in C++ code (like the extended LLVM API).
Why is rust bad for unsafe? Isn't that like a superpower to be able to "override" the borrow checker on a specific line instead of having a whole unsafe codebase?
Rust is really bad with global side-effects from one object to another. In some regards, Rust is a really good object-oriented language (in the "original" definition) because it has strong typing guarantees, first-class interfaces (traits) and the best (de)serialization library i've ever encountered [0].
But Rust being parallel-friendly by default will prevent you from all sorts of patterns common in dynamic languages (JS, Python, PHP), where all objects hold mutable references to any other object (without clear hierarchy/interface) and anything can be mutated from any part of the program. In single-threaded programming, this kind of object tinkering can lead to unexpected behavior (not undefined behavior) where one tiny function somewhere will have crazy side-effects that will break some other part of the program. But in parallel programming, it's certain to cause all sorts of quirks due to race conditions (two threads accessing/modifying the same data).
In that sense, Rust takes some inspiration from functional programming, and side-effects are declared with mutable pointers (&mut Type
). So it's still technically possible to do everything you do in a dynamic language in Rust, but the language doesn't make it especially easy. I personally think it's a very good trade-off. Although i'm sometimes bored to write more boilerplate, i can't stress enough how relaxing it is to have your code working expectedly as soon as it compiles. It feels like a superpower.
[0] serde. Seriously, serde is so powerful and standard across the Rust ecosystem that writing serialization code in golang/python feels very clunky after trying that out.
warning: very confusing comment, this is a complicated topic
Rust, in a vacuum, can do anything basically perfectly (yes, even unsafe code, at least it's clearly marked as so). However, the vast majority of people aren't writing rust in a vacuum, they are using the "rust ecosystem", and, for better or worse, the "rust ecosystem" is mainly useful for two things: making back-end servers and standalone libraries (look into rust's history and you'll see why)
I hear a lot about how the things that Rust is not good for, JIT compilation with a garbage collector is usually the best solution, and vice versa. How true is this?
JIT compilation is good for embedded programming languages, rust isn't one so they don't support it (although JIT compilation is a very complex topic, especially when talking about use cases)
Garbage collecting is a simple way to ensure memory safety without the whole complex memory layout of rust, rust doesn't have it because it does not need it
Programming languages are just tools, use the right one for the job.
It's pretty lousy for exploratory programming, where you load your current application to memory, then start adding a new function onto it, hot reloading on every change and testing your changes and external interfaces in the REPL. Rust's best approach to this seems to be evcxr, which is quite janky. So in practice you probably end up writing unit tests or dummy binaries that replicate similar behavior.
Examples of language implementations that are good at this: python (especially with ipython), most common lisp implementations.