Hacker News new | ask | show | jobs
by jeroenhd 1481 days ago
Rust isn't hard for the things you try to solve in Rust.

The author compares Rust code with Go code. Those two languages serve entirely different purposes, with entirely different mechanisms behind it.

Go does what the author does with ease because the runtime fixes all the complicated parts for you. You tell Go what you want and it'll try to solve all the memory management/threading/memory safety issues for you, usually succeeding.

Rust doesn't do that. Rust expects you not just to tell it what you want, but also how you want it to happen.

I think comparisons between Rust and Go/C#/Java are what will really trip up a beginner. Rust has a lot of nice features found in higher level languages, but it's decidedly not a higher level language. Rust operates in the space of C and C++, where a small mistake can cause memory corruption no debugger will ever be able to unravel, but where a well placed byte of padding can accelerate a program by as much as 30 percent.

I think the difficulty in Rust lies in that it will enforce correctness. Competing languages are less strict about that, especially when it comes to threading. You tell them a piece of memory is safe to use across thread boundaries and they'll believe you, and most of the time you can rely on race conditions not screwing you over perfectly well. A C program can be short, fast, and clear, as long as you leave out the error checking and resource management in case of failures; with Rust you often don't get that luxury. Writing correct code is a slow, tedious, painful experience, and in Rust you'll have to live with that pain (unless you throw around unsafe{} everywhere).

I believe that teaching programming should follow a bottom-up approach, but many others disagree. If you've dabbled in assembly, done some multi threaded C(++) and experienced the challenges in low-level code, Rust should be enjoyable enough to learn after cursing at the borrow checker a bunch of times. If you're a top-down learner, though, you'll run face-first into low-level problems and their complexities for seemingly no good reason.

3 comments

In fairness, for the kind of software that C++ is particularly suited, the idiomatic software architecture is thread-per-core, which has the distinction of being almost entirely single-threaded at the code level. Race conditions aren't a meaningful concern because data virtually never crosses thread boundaries. The bigger issue, particularly and mostly for C code, is object lifetime management.

If maximum performance, whence thread-per-core architectures originate, is not the objective, then GC languages start to become more attractive and C++ may not be the right tool for the job. And in those cases, Rust may not be either.

You're not wrong, but the crux of the problem described in the article is that Rust's object lifetimes are very hard to get right (even for the people working on the compiler) when working with cross-thread code.

I'm no professional Rust dev but I wouldn't have written the code like this; I know Rust isn't particularly suited for this style of callback mechanism and I know not to try and force this paradigm into Rust the same way.

For example, I think the author would have had a much raiser time if instead of passing async futures, they'd use channels or some other message passing mechanism in combination with a bunch of blocking threads to communicate events. Such a mechanism would also translate into Go quite easily (less so for other languages, though).

This example was deliberately picked to show a complex problem with writing Rust. I don't think this represents a challenge you'd face very commonly if you were programming Rust all day, at all. It's not bad criticism, but it appears to imply a much wider problem than there really is, in my opinion.

I don't really know what kind of programs require such an elaborate callback system commonly enough where it even makes sense to use Rust. C#, Java, and Go are fast, easy to write, and each have libraries to do almost anything you want. That 10-30% speed boost you can achieve with well-written Rust is probably not really worth the effort, especially with upcoming AOT compilation features in C#.

Rust isn't a solution to all problems, and neither is any other programming language.

> I think the difficulty in Rust lies in that it will enforce correctness. Competing languages are less strict about that, especially when it comes to threading.

Enforcing correctness at compile time is not the only way to insure correctness.

Some do enjoy solving language puzzles (so choose Rust) and some prefer thinking before coding and prefer solving design puzzles. I personally prefer that latter, as the 'hard' problems are intellectually interesting, solving them is satisfying, and over the years the design lessons build upon each other. At which point you don't need a Mommy Dearest Compiler to ensure correctness.

This is the "don't do anything wrong" model of software development, and while it works well for some, we have enough experience as an industry to know it doesn't scale.

Crucially, it's hard to prove whether or not you've actually solved whatever design issue you wanted to overcome. Such a proof usually would entail some sort of analysis of the program as written (because it may actually differ from your design). To perform this analysis, you may want to annotate the lifetimes of the various objects as they are declared, so that you can track (for example) that some memory is not accessed after it is freed, or any other number of issues.

This lifetime analysis as you would imagine can be very tedious and complicated, so you would perhaps want to automate the process. And that's essentially why Rust's borrow checker exists. It's almost inevitable that it should exist imo. Seems completely obvious after the fact.

To quote threatofrain:

> All type systems will have meaningful and true propositions which are apparent to the programmer but not yet to the language team... Some of what the author is complaining about matches my conversations with people who aspire to be Rust library authors — that you're often trying to hijack the type system because you <do> actually know better.

Rust catches some problems (like data races and use-after-free). Safe Rust translated into correct C++ is still correct. Rust also fails to catch some problems (like preventing out-of-bounds indexing at compile time); admittedly idiomatic C++ fails to catch bounds errors at runtime. And when encountering problems that Safe Rust cannot solve (like the generic lifetime quagmires in the original post), C++ often makes it possible (and easier than esoteric programming languages like Unsafe Rust) to solve the problem correctly in the current situation; though admittedly, ensuring you haven't missed any UB cases, and validating that your assumptions don't break later on, is difficult (Unsafe Rust is better at marking unsafe code for future readers).

> Enforcing correctness at compile time is not the only way to insure correctness.

> some prefer thinking before coding and prefer solving design puzzles

Yeah, you just need to guarantee that person working on it, considered all edge cases, had uninterrupted time to think, thought about how the edge cases interact, didn't make a single mistake, wasn't sleepy, under influence of substances, and perfectly wrote it into the program without a single semantic error (off by 1).

Easy.

That's why I code in Malbolge Lisp CodeGen that outputs Brainfuck.

If you can solve all your problems by thinking before writing code then you will never see a compiler error, unless you see a compiler bug. If you're solving your problems beforehand by thinking about them but end up solving language puzzles, you clearly haven't thought enough.
If the benchmark is "projects that are known for idiomatic C", such as Redis or Sqlite, we know that even they introduce memory corruption errors that lead to vulnerabilities every now and then. You're not better than them.
And yet those C and C++ projects continue to be very popular and successful. :-)
>I think comparisons between Rust and Go/C#/Java are what will really trip up a beginner. Rust has a lot of nice features found in higher level languages, but it's decidedly not a higher level language. Rust operates in the space of C and C++, where a small mistake can cause memory corruption no debugger will ever be able to unravel, but where a well placed byte of padding can accelerate a program by as much as 30 percent.

I agree. But if you use Rust for web programming, it is fair to compare it with C# or Java. On the other hand, C and C++ feels easier to read and write, a bit more productive than Rust.

So the question is vs C# and Java: is the performance worth the pain and loss of productivity?

And vs C and C++: is the guarantees made by Rust worth the pain and loss of productivity?

I'd argue that in some cases the answer can be yes, while in others it ca be no. So, there's no universal good or bad choice, it really depends on project, team, budget and many more.

I don't know why you'd possibly want to use Rust for web programming, to be honest. When you add a full stack of databases and entities, Rust barely becomes faster than ASP.NET or Spring Boot. I messed with it for fun, but I don't think I'd pick Rust as a web server language any time soon.

The only reason I can think of is the WASM space, which Rust lends itself very well to, to reuse the same entities and data structures in the front end. Then again, you'll end up writing a terribly bloated web UI and other languages have similar bindings.

I think for new projects where C++ makes sense, Rust probably makes more sense. There are some edge cases (if you expect to be operating on trees in memory, for example, or if you're interfacing with libraries written in other languages) but I think Rust is generally better for such system tools. That assumes that you have in house Rust devs, of course; if you're a C++ shop, you'll have to teach everyone a new language before the switch makes sense.

The C(++) crowd is difficult to teach other languages because they, more than any dev group I've encountered, seem to have a larger amount of vocal people who think their code is perfect, they won't ever produce bugs, and all those compiler errors warning about failing edge cases are unnecessary because they know best.