Hacker News new | ask | show | jobs
by skohan 1896 days ago
Rust is currently my favorite language for personal projects. It's got a very good value proposition in terms of giving some high level features along with low level control and performance, great compatibility story, and the tooling and community is absolutely great.

However, as the manager of a technical team, I would not choose it for professional projects. The learning curve is very steep, and to reap the benefits you have to pay a high cost in terms of accepting additional complexity. I think Rust is a good choice for some professional use-cases, for instance performance-sensitive applications like embedded, or safety-critical applications. But for many applications, like your average webserver for example, I believe you will lose productivity and have a hard time hiring if you choose Rust.

For my team, we chose Go because it is easy to hire and onboard people, the tooling and compatibility story is plenty good enough, and due to the complexity ceiling, there's only so much damage a developer can do in terms of taking the codebase in a bad direction.

Rust is a capable language for a professional setting, but it is also a language ideally suited for people who enjoy indulging in complexity and tricky problem solving, and this is not the right choice for every project or team.

4 comments

As an indie developer, switching from Go to Rust for web APIs was extremely satisfying. At first there was a productivity loss, but after a few months the productivity was better than Go thanks to Rust's functional features which make it extremely pleasant to write business logic, and the type system catching bugs during development instead of production.

You can read more about the experience here: https://kerkour.com/blog/rust-for-web-development-2-years-la...

The only thing I'm missing is Go's awesome TLS support (with autocert & co).

That being said, I understand your point regarding hiring: a friend of mine totally refuses to learn Rust because the syntax looks not good to him.

How much of a productivity boost would you’ve gotten from java or kotlin which provides a lot of those features with all the complexity and build times?
I did a bit of Java and Kotlin for Android development, but never for web APIs so I can't really tell, but one thing I'm sure is that Rust's build system and dependency manager (Cargo) is far more pleasant to use than the experience I had with Java.

Other great things are the ease of packaging with Docker, the low resources usage which helps to keep Heroku's bills small, and the excellent IDE support.

For web work, are there benefits to using Rust over a garbage collected language that also benefits from a similar type system?
Do you have a specific language in mind?

Honestly, I don't feel that I spend time managing memory in Rust: I use ARC pointers for long-lived shared object (Database connections pool, mailer...) and otherwise the ownership is pretty straightforward during the lifecycle of a request, data is moved from the top layer (HTTP handlers) to the bottom (Repositories to access Database) and back for the response.

From my limited knowledge, Rust's type system is quite close to the ML family-- so perhaps F# would be a general-purpose GC'd language equivalent?

I am mainly interested to know how much overhead time is spent appeasing the borrow checker and managing memory that would otherwise free up mental cycles if a GC were available . The async story for Rust also seems confusing (but I hold my hands up and plead ignorance on this count).

> I use ARC pointers for long-lived shared object (Database connections pool, mailer...)

Does that mean you basically enforce sequential database reads? Seems like a bottleneck if your server is concurrent

Arc just allows sharing between multiple threads, it doesn't prescribe any sort of locking.

If they're sharing a connection pool then each HTTP request would get its own connection out of the pool, and they'd be concurrent (access to the pool may or may not be serialised depending on the pool's details, sqlx is internally mutated not externally locked for instance).

The mailer might be behind a mutex (its access completely serialised), or the "mailer" might just be the input side of a queue / channel, and the actual mailing work be done in a separate process (that seems way more likely than bounding the request on sending emails really).

“Complexity is the enemy of execution.” - Tony Robbins

I don’t think Tony ever did any coding, but you’re right that it still applies here.

A google search shows complexity has many other enemies: security,reliability, agility, and progress itself.

In most cases I think this is right, use Go and simple tools whenever you can. There are cases where complexity is unavoidable though, like when you’re trying to land a rover in a precise location on Mars, where the sky crane makes sense, but don’t introduce the complexity when it’s unneeded.

Rust is more complex than Go, but when you are dealing with the inherent complexity in a problem, sometimes Rust can allow you to make the decision to take a larger chunk of that complexity and handle it at compile time (instead of at runtime with a crash->debug->fix feedback loop).

So you get sometimes stressful and frustrating writing sessions, coupled with more confidence in the runtime correctness. Also the more expressive type system can allow you to more closely match the domain, which helps when you add new features in the future as you can rule out certain invalid states or interactions.

I often feel you need to know the right conventions to write correct Go programs, where as Rust will just tell you you cannot run it.

I would get things done faster in Go for sure, but the compiled artifact would likely have more issues during runtime.

> don’t introduce the complexity when it’s unneeded.

Prediction is hard. Sometimes I start with something simple, and it ends up growing in scope until it's quite large and complex. Other times I start with something simple, and it stays simple.

So, what's worse, using a complex and/or strict language for something simple, or a simple and less strict language for something that ends up being very large and/or complex?

Interestingly, some people will probably read that last paragraph as my trying to make a point through implication. I suspect different people will assume I'm saying completely opposite things based on their own experience. Really, it's just hard, you get better at making the right choice as you go along, but you'll never always get it right.

> safety-critical applications

Nope, Rust is also not suitable for these tasks since it doesn't have a certified toolchain (e.g. ISO26262 for automotive or DO-178 for aerospace).

> embedded

Current LLVM-based compiler lacks support for some platforms (e.g. Xtensa for some ESP32 MCUs).

'Embedded' doesn't require supporting everyone's favorite microcontroller. Even if ESP32 is really popular, there's plenty of others. OBTW experimental xtensa backend is landing in upstream llvm lately.
That were just the examples when Rust is not really suitable as a drop-in replacement for C/C++. Of course, if your platform is well-supported by Rust compiler and you don't have to write safety critical applications, then using Rust is just a matter of choice.

I don't argue whether it's good or bad language. It's a quite interesting one with its own pros and cons. If it fits you, then it's great, but you should know its limitations.

It took me two hours to compile the esp32 fork of rustc on a 2015 macbook pro. Not ideal, but it works just fine.
That's very interesting! Do you have any pointers on how to do that?
Certified toolchains only factor into play in DAL A for DO178. Either that or manually inspecting the compiler output and tracing it to the source.
Rust and Go are not in the same domain space, so it's a false dichotomy to compare the two.

It seems in your use case, Java would have been a viable (and potentially superior) alternative to golang.