Comment by mprovost
3 years ago
This is a really interesting observation: "Stateless programs like command line tools or low-abstraction domains like embedded programming will have less friction with the borrow checker than domains with a lot of interconnected state like apps, stateful programs, or complex turn-based games."
In my book [0] teaching Rust, I manage to completely avoid using lifetimes. Perhaps this is because it reimplements a command line utility (head, wc, cat, ...) from the original BSD sources in each chapter and none of them are stateful. Most of the classic Unix utilities just process streams of bytes and barely use data structures at all. Many don't even call malloc().
It feels like Rust is a good match for the problem domain but I never made the connection with the stateless nature of these tools before. This bodes well for eventually replacing the coreutils with modern Rust implementations.
This was a particularly fascinating realization of mine a few years ago. I realized that for certain domains, the borrow checker was a dream to use (command line tools and scripts, ECS games), and in others it wasn't so much (GUI, turn-based games).
I dove a little deeper, and the stateful/stateless distinction seemed to be the best rule-of-thumb for predicting how much one would be in conflict with the borrow checker's preferred styles. It was also reminiscent of functional programming languages, in a way.
I suspect this is why most discussion comparing Rust to other languages devolves so quickly, and has become so polarized: it depends on the domain. Users who try to use it for some domains will hate it, and users who try it on other domains will love it.
This is also why I recommend newer Rust programmers to not be afraid of using Rc/RefCell in certain domains. Some domains don't agree as much with the borrow checker, and Rc/RefCell can make the architecture a lot looser and easier to work with.
I made almost this EXACT comment a few days ago, nice to see I'm not crazy
https://news.ycombinator.com/item?id=34386997
For 98% of tasks Rust is incredible. But modeling those 2% of problems that don't fit neatly into Rusts domain, the level of "unsafe", "PhantomData", and advanced type-system hacks you need feels obscene when I can use some pointers in C++.
I will openly say I think Rust is an all-around better language. If you are building general-purpose systems software, or just want to write fast software, use Rust.
If you are fiddling with bits, doing low-level concurrency, writing a GC/Memory Manager etc, or have self-referential datastructures like graphs or trees, you're going to have an order of magnitude easier time writing it in C++. (Rust-experts excepted)
> But modeling those 2% of problems that don't fit neatly into Rusts domain, the level of "unsafe", "PhantomData", and advanced type-system hacks you need feels obscene when I can use some pointers in C++.
I don't agree. This happens only if you want to not make any compromises on the performance and stay on the safe side, which is a goal unreachable for the majority of languages. Often an Arc/Rc/Refcell + a few clones makes the borrow checker shut up and the code is not more complex than it would be in another GCed language. And often still faster (although YMMV), because those few clone calls may totally not matter, and Rust compiler runs circles around compilers/runtimes used in most popular GCed languages like Java, Go, JS or Python.
You can also use raw pointers which is not different from using raw pointers in C++. I have no idea why some people say using pointers in C++ is fine but writing some parts of a program in unsafe block in Rust is a problem.
> I suspect this is why most discussion comparing Rust to other languages devolves so quickly, and has become so polarized: it depends on the domain.
The problem is that Rust proponents argues that Rust is better for all domains. I don't think anyone says that Rust doesn't have a place, the source of controversy is whether every low level programming task is best done in Rust or not.
Not sure if it is strictly better, probably not, but it is actually damn close to being better in most domains and not worse in the others. I've been developing a GUI app in Rust, and even though this is an area considered widely a very bad fit for a borrow checker and a non-OOP language like Rust generally, the borrow checker was never an issue for me (and it still helped in a few places to get e.g. concurrency right). An occasional Rc/Refcell here and there does not outweight the other advantages. Definitely not any worse than Java Swing in terms of productivity, but way better in terms of the end result (which launches in 0.1 seconds, has no visible lags and uses native window controls).