Hacker News new | ask | show | jobs
by m12k 2538 days ago
I mean, C++ programmers are exactly the correct audience for a language like this - there are many other options for memory safety if you can live with a garbage collector[1]. But writing safe C++ is much, much, much more complex than than writing okay-ish C++. The way I see it, Rust has basically taken a lot of the best practices required to write sane C++ (e.g. RAII) and formalized them in a way where the compiler can enforce them. That means in order to write ANY Rust code at all, you have to adapt a lot of best practices all at once. That's not very beginner friendly, and will probably lead to cognitive overload in most - you certainly don't get the same freedom you get in other languages where you can implement something in dozens of ways, because most of those ways won't compile here. So I'm not saying they shouldn't keep working on the ergonomics and learnability of the language, but I think a lot of these complexities are essential to the task of writing sane programs while dealing with raw memory, and the fact that they have been named, formalized and checked by the compiler is entirely a good thing - and if that means the programmer has to know about them, then that's ok.

[1] Sidenote - I find it really fascinating how Rust can also use the stronger static checks to prevent things like race conditions in a way few (/no?) other languages can.

5 comments

> But writing safe C++ is much, much, much more complex than than writing okay-ish C++. The way I see it, Rust has basically taken a lot of the best practices required to write sane C++ (e.g. RAII) and formalized them in a way where the compiler can enforce them.

A concrete example that I've run into recently when trying to write C++ code. I figured that, for safety reasons, I needed to make my type be move-only. I then had to spend about two hours trying to figure out why the program was blowing up. The reason was that I was reusing the variable after moving from it, and the compiler never gave any warning (even on -Wall -Werror) telling me that what I was doing was wrong. In Rust, the same situation would be a compiler error.

Yep. As much as people extol lifetimes, my personal opinion is that Rust's aliasing rules are its true golden goose. C/C++'s lax approach to aliasing causes a whole host of issues that Rust is able to avoid by being more strict.
Using a moved-from object in C++ doesn't produce any warnings because it isn't an invalid operation. The standard library types make very limited guarantees about the state of moved from objects (generally just that it remains valid to assign to them and that the object's invariants still hold), but even then it's valid to reuse them as long as you first do something that ensures they're in a known state.
Rust and C++ have rather different concepts of moving. A moved-from Rust object is entirely dead, cannot be used, and will not be dropped. A C++ moved-from object is alive as far as the language is concerned, and the destructor will still run. The move operation and the destructor need to cooperate to avoid crashing. This often adds overhead.
C++: null pointers were a mistake, so we're introducing null objects too.
clang-tidy should catch this and warn about it at compile time.

The two hours seems on the high-end, if someone's able to e.g. use ASan and the program is crashing reproducibly.

Thing is many of Rust features could probably be enforced with a static analysis tool, which a large majority unfortunately ignores.

So you either have a C++ shop where everyone is on board regarding security, with the caveat of third party dependencies, or no one cares and writes something along the lines of C with C++ compiler, without any kind of static analysis.

Relying on external tooling means it usually gets ignored if it is not enforced. After all C's first version of lint goes back to 1979.

Sadly JetBrains latest questionnaire results prove exactly that.

So having safety as integral part of the language semantics matters a lot. Defaults matter.

> Thing is many of Rust features could probably be enforced with a static analysis tool, which a large majority unfortunately ignores.

But it definitely can't be? There are plenty of open source projects (Chromium, Firefox) that develop and leverage state of the art static analysis tools and best practices. It's very clearly not enough, and the costs (built/ test time) are really significant.

Our day to day software development practices still fail short of what design by contract, MISRA, AUTOSAR, DO-178B and similar offer in terms of delivered quality.

Only with further increase in lawsuits and returned faulty software, like in other commercial areas, will companies start paying attention to QA budgets.

MISRA and DO-178B deliver more on the illusion of quality then anything else. They are desperate attempts to tame software complexity. But they don't fundamentally solve anything.
How come? They aren't perfect, but they seem to at least make Ada/Pascal out of C.
I would agree with the parent. It's a while since I did my last MISRA project, but I know that it doesn't even prevent basic memory safety issues or leaks. It's more a set of coding guidelines that prevent some kinds of errors than robust tool that will reliably detect those.

Static analyzers work better, but often have a terrible signal-to-noise ratio. I think Rust can on average prevent more errors than all of those things out of the box, which is impressive.

The downside is obviously the increased complexity, and that it sometimes feels one is forced to work around the limitations of the "static analysis tool". Which likely comes from the fact that the borrow checker is some kind of analysis tool, where the annotations are directly included into the language.

Please elaborate with examples.

Since most new cars are Internet connected and have whole hosts of complex safety features dependent on software correctness, I sure do hope you are wrong about this.

MISRA and similar standards are incredibly limiting. For example MISRA forbids dynamic allocation.

They not only make writing software a lot more difficult and expensive, they also restrict the kind of software you can write.

Do you think QA budgets would actually help here? At least the way I know it QA and development is separated, and no matter how many QA people you hire, many developers brush off QA until later.

Considering Rust shows can enforce so many things in the compiler, to me it's clear that a better compiler/language is a better way to address this problem than QA people.

Also the built in testing with cargo test makes TDD so much more attractive.

They don't, but others do. Sound static analysis tools like TrustInSoft (https://trust-in-soft.com/) guarantee no undefined behavior (array overflow, use-after-free etc.).
That's basically a reduced form of program verification, and requires a lot of developer help. You end up programming in a language that looks like C but isn't. It is not simply a matter of throwing a pile of C++ code at the tool and fixing a few errors it reports.
That depends what you mean by "a lot". The effort required is significantly less than a rewrite in a safe language. If we're talking about properties that safe languages can verify, i.e. simple, local, ones (like memory safety), verifying those in a sound static analysis tool is not hard, either.
Rust is not a collection of C++ best practices, formalized. It has its own "personality", borrowing ideas from several other languages and unfortunately these other ideas happen to be alien to most C and C++ programmers.

There was a funny discussion on the Rust subreddit, where even some language contributors have started having doubts about that complexity. One of them was trapped in his own programming language theory ivory tower, the other was trying to convince them that they are losing developers if they keep adding stuff to the language.

That discussion was a clear hint that the Rust developers don't have C and C++ programmers in mind when designing Rust. They have their own ideas about how a modern systems programming language should look like, and they're doing that. Perfectly fine, but we need to correct the misconception that C or C++ programmers will rush en masse to learn Rust.

Rust developers have actually been very cautious about integrating the 'usual' sorts of PL-theory driven features in Rust. The PL theory of Rust-like languages is still in its infancy in many ways, but it has already usefully informed the design of library features like e.g. Pin<>, as well as eased the understanding of seemingly ad-hoc language features like so-called 'internal' mutability, which - as it turns out - can be described via a remarkably simple theoretical basis.
> prevent things like race conditions

Rust can prevent all data races but not all (any?) race conditions. Related question: can you use the type system to catch a subset of race conditions?

https://stackoverflow.com/questions/49023664/could-software-...

Also, most c++ code is just...ugly. It can really assault the senses, looking at some of the template & indirection abstractions, etc. Rust just looks better, from what I've seen.
While this is very subjective, I tend to agree. All codebases I've read and worked on in C++ were eyesores in all but the simplest places.

That being said, I think Rust macros are much worse compared to C++, if you ignore templates.

Don't get me wrong, I really like Rust. I just think that it's macros make for some of the most unreadable code I've ever seen.

This seems kind of inevitable with macros. They help the macro creator write code with new, "better" abstractions, but readers of the code have to learn the semantics of the macro before they can understand what's happening. The built in mechanisms of a language are familiar to those new to a program's code but macros usually hide something significant (otherwise why have a macro).

I've written fancy macros for assembly language programming to support my own looping, iterating, argument passing etc. but I noticed that the other programmers on the team weren't interested in using them.

On the other hand, I'm so grateful to John Wiegley for his use-package macro for emacs lisp.

> readers of the code have to learn the semantics of the macro before they can understand what's happening.

They always have the alternative of reading the expanded code, which is very similar to what the author of the macro could have written by hand instead of the macro.

I had the same feeling with macros intially, but once you wrote a few of them it's ok, and they provide many more guarantees than pure text preprocessing of C/C++.
Rust is not a pleasant language to read tbh: https://github.com/SergioBenitez/Rocket/blob/v0.4/core/http/...

Clean C++ 14/17 is less cluttered.

That file looks quite readable (ignoring the comments - which again looks very readable in color syntax highlighted documentation).
I think it's the numerous single character lifetime annotations that they are referring to. I agree that this particular bit of code is somewhat hard to parse (as a rust amateur), but that might be alleviated quite a bit with some less terse naming.