Coming from mostly a web programming background, it certainly feels more low-level than PHP/JavaScript, but not by much, at least for web/backend work. You can still code without having to delve into low-level/unsafe coding, but that stuff is available to you if you need to squeeze more performance out of your app.
The biggest hurdles for me (I'm still learning) compared to JS is the borrow checker, lifetimes, and strings (they are handled very precisely in Rust).
The benefits are huge though. My time coding in JS feels like 50% coding & writing tests, 40% debugging my app, 10% reading documentation. The "debugging" portion can sometimes be painful when fixing race conditions. Rust on the other hand, feels like 30% reading documentation, 30% coding, 30% getting my code to compile, and the other 10% debugging/testing my app. For the most part, if your code compiles and your logic is correct, your app is guaranteed to run the way you expect it to.
Coming from a systems/DSP background, Rust feels at times "higher level" than C/C++ (especially w.r.t. the crates ecosystem, although I suspect a large part of that is its youth and general instability), but mostly akin to writing C-with-classes-style C++ with static analysis baked into your build script.
The "high level" stuff baked into Rust makes it great for "low level" tasks, notably imo:
- Tests (and micro benchmarks, with nightly) alongside code, without dependencies or build system hackery.
- An incredibly powerful macro system, supplanting a lot of the templated code generation I've done in C++ which is a nightmare. Not that proc macros are perfect (yet) but at least they're legible.
- If you've ever tried to do away with dynamic dispatch via templates in place of inheritance, then Rust's generics with trait bounds are an absolute godsend
- A gosh darn dependency solution (now with custom repositories on stable!) makes dependency hell is less hellish
I always ask people that say this if they tried using reference counting during the times the borrow checker was too difficult. Rust supports it. There's going to be some performance hit. Most in your situation use GC'd languages, though. So, it seems like Rust with ref counting could still make for easy web development where you get Rust benefits in borrow-checked code but rest is still safe and quick to write.
That's the theory anyway. I haven't gotten any data from people trying that on real-world apps, though. There could be non-obvious problems with it.
I suspect that it might be easy to port most code written in high level languages to Rust if you're willing to use "Rc" or "Arc" liberally. The drawback is Rc does not support cyclic garbage collection (AFAIK), so the resulting code would often have memory leaks.
Weak refs are a way to work around reference counting cycles, but cyclic garbage collection is a more complete solution and it's a core feature of nearly every high level language.
It seems like people who often use Rust's Rc and Arc will long for some way to implement generic cyclic garbage collection. I don't yet see a way (other than unsafe code), but I may be missing something.
The biggest issue, and I fall back on Rc a lot, is that often you end up needing Arc. Especially in async code, where Send is generally desirable. But it's a great way to get around annoyances with shared memory.
The biggest hurdles for me (I'm still learning) compared to JS is the borrow checker, lifetimes, and strings (they are handled very precisely in Rust).
The benefits are huge though. My time coding in JS feels like 50% coding & writing tests, 40% debugging my app, 10% reading documentation. The "debugging" portion can sometimes be painful when fixing race conditions. Rust on the other hand, feels like 30% reading documentation, 30% coding, 30% getting my code to compile, and the other 10% debugging/testing my app. For the most part, if your code compiles and your logic is correct, your app is guaranteed to run the way you expect it to.