It's a shame imo that it's not seen as a "cool" option for startups, because at this point, the productivity gap compared to other languages is small, if nonexistent.
It's a shame imo that it's not seen as a "cool" option for startups, because at this point, the productivity gap compared to other languages is small, if nonexistent.
Rust feels like walking on a minefield, praying to never meet any lifetime problem that's going to ruin your afternoon productivity ( recently lost an afternoon on something that could very well be a known compiler bug, but on a method with such a horrible signature that i never can be sure. in the end i recoded the thing with macros instead).
The feeling of typesafety is satisfying , i agree. But calling the overall experience a "joy" ?
> recently lost an afternoon on something that could very well be a known compiler bug
With respect, at two months, you're still in the throes of the learning curve, and it seems highly unlikely you've found a compiler bug. Most folks (myself included) struggled for a few months before we hit the 'joyful' part of Rust.
Rust has a horrid learning curve
I've programmed for decades in many languages, and I felt the same as you
Persevere.
Surrender! to compile
Weather the ferocious storm
You will find, true bliss
Simply using axum with code using multiple layers of async was enough.
But then again, it looked like this bug (the error message is the same), however at this point i'm really unsure if it's exactly the same. The error message and the method signature was so atrocious that i just gave up and found a simpler design using macros that dodged the bullet.
However, at the moment i still feel i'm using a huge amount of layers upon layer of complex type definitions in order to get anything done. Just using an object's reference across async calls in a safe manner leads to insane types and type constraints, which read like ancient egyptian scripture. And at every layer, i feel like changing anything could blow everything up with lifetimes.
The language has this very special feel of something both advanced and extremely raw and low-level at the same time. It's very unique.
Also, it’s worth saying, you probably don’t need async.
However, at some point you have to ask yourself why you're accepting to face all those challenges. Is it worth it ? When was the last time i faced a race condition when developping a backend ?
The reason i started with rust was for a very specific need on building a cross-platform library (including wasm), and that was a good justification, and i'm happy that i did. However now that i'm using it for the server as well and face the same kind of challenges, i seriously question whether this is a wise choice.
Then you want to declare an async function that takes an async closure over that dependency. And you end up with a total garbage of a method signature.
As for async, the ecosystem for server-side is totally filled with async everywhere now. I don't think it's realistic to hope escaping those issues anyway in any real-world project. i thought i might as well learn to get comfortable with async.
Go felt the same way (but with a much lower order of magnitude) : you feel like bumping into language limitations, but once you learn to do it "simply" in go, your style will have changed into something much more elegant.
As for the bug in question, it has been quite "popular" for about 5 years now, and is actively tracked : https://github.com/rust-lang/rust/issues/110338. Nothing really weird. Just async hitting the limits of the current rust design.
And macros are a part of that!
I'm still unsure if it's a good thing in general, because as a general rule meta programming is always harder to debug. But for simple macros it seems like a nice trick.
Determinism.
With Rust lifetimes, you can statically prove when resources will be released. Garbage collectors provide no such guarantees. It has been shown that garbage-collected languages have a space-performance tradeoff: you need five times as much RAM to achieve the same performance, even with a "good" GC, as the same program with explicit memory management:
The other one is thread safety, due to the compiler-enforced ownership semantics that prevent threads from accessing shared data unless they do so in a well-defined way.
Other languages use constructs like context managers or try-with-resources to capture this, but these constructs are very limited and make it very hard or impossible for these resource types to be put into a container and passed between threads. In Rust this is trivial and actually just works.
Garbage collectors usually give much weaker guarantees about when objects are freed, so destructors (which are sometimes not even available, like in JS) might only be called much later. You can't rely on a GC to unlock a muted for you. But in Rust it happens when the guard is dropped, always, immediately after it's last needed.