In a Software Engineering Daily podcast hosted by Kevin Ball, Steve Klabnik and Herb Sutter discuss several topics related to Rust and C++, including what the languages have in common and what is unique to them, where they differ, how they evolve, and more.
The perfect language does not exist
For Klabnik, besides the common idea about Rust being a memory-safe language, what really makes it different is its taking inspiration from ideas that have not been popular in more recent programming languages but that are well-known in the programming languages space.
Sutter, on the other hand, stresses the idea of zero-overhead abstraction, i.e., the idea you can express things in a higher-level way without paying an excessive cost for that abstraction. Here Klabnik echoes Sutter considering how the primary mechanism Rust achieves "zero-cost" abstractions is through types:
[Klabnik] Basically, the more that you could express in the type system, the less you needed to check at runtime.
But not all abstractions are zero-overhead, says Sutter, bringing the example of two C++ features that compilers invariably offer the option to disable: exception handling and runtime typing.
[Sutter] If you look at why, it's because those two can be written better by hand. Or you pay for them even if you don't use them.
So that brings both to the idea that language design is just as much of an art or a craft, heavily influenced by taste and a philosophy of what a language ought to be, so we are not yet to the point where a language can be the perfect language.
Areas of applicability
Coming to how the languages are used, Sutter says they have a very similar target, with ecosystem maturity and thread-safety guarantees being what may lead to choose one over the other. Klabnik stresses the fact that Rust is being often adopted to create networked services, even at large companies:
[Klabnik] Cloudflare, for example, is using Rust code as their backend. And they power 10% of internet traffic. Other companies [...] had some stuff in Python and they rewrote it in Rust. And then they were able to drop thousands of servers off of their cloud bill.
Sutter highlights the ability of C++ of giving you control of time and space and the richness of the tool ecosystem that has been built around C++ for 30+ years, while Rust is comparatively still a young language.
[Sutter] Right now, there are things where I can't ship code out of the company unless it goes through this tool. And it understands C++ but it doesn't understand Rust. I can't ship that code. If I write in Rust, I'm not allowed to ship it without a VP exception.
How languages evolve
When it comes to language evolution, the key point is when it is worth to add complexity to the language for a new feature, which is not always an easy decision. In this regard, one of the things Klabnik praises about C++ is its commitment to backwards compatibility "within reason".
[Klabnik] I'm not saying that the rust team necessarily doesn't have a similar view in the large. But there's been some recent proposals about some things that kind of add a degree of complexity that I'm am not necessarily personally comfortable with.
Sutter mentions how the C# team at some point decided to add nullability to the language and, although this is surely a great thing to have, it also brought hidden costs:
[Sutter] Because there's complexity for users and the complexity in the language now but also for the next 20 years. Because you're going to be supporting this thing forever, right? And it's going to constrain evolution forever. [...] It turns out that once you add nullability [...] the whole language needs to know about it more than you first thought.
To further stress how hard it is to take such kinds of decisions, Klabnik recalls a case where he fought against Rust's postfix syntax for await
, which differs from most other languages where it is prefix:
[Klabnik] Now that I'm writing a lot of code with async and await, gosh, I'm glad they went with postfix await. Because it's like so, so much nicer and in so many ways.
The power of language editions
One way for the Rust Core team to manage change is defining language editions to preserve source compatibility when new features are added.
Sutter shows himself to be interested in understanding whether this mechanism could have made it possible to introduce C++ async/await
support without needing to adopt the co_async
and co_await
keywords to prevent clashing existing codebases. Klabnik explains editions are a powerful mechanism to add new syntax, but they have limitations:
[Klabnik] Changing
mute
tounique
as a purely syntactic change would absolutely work. Because what ends up happening is when it compiles one package for 2015 and one package for 2024, and 2024 addsunique
or whatever, it would desugar to the same internal [representation ... but] you can't like add a GC in an edition, because that would really change the way that things work on a more deep level.
Similarly, the Rust standard library is a unique binary and thus you have to ensure it includes all functions that are used in all editions.
Klabnik and Sutter cover more ground and details in the podcast than can be covered here, including the importance for C++ of its standardization process; whether having an ISO standard language would be of any interest to the Rust community; the reasons for and implications of having multiple compilers vs. just one in Rust's case; how to deal with conformance; and the value of teaching languages like C++ or Rust in universities. Do not miss the full podcast if you are interested in learning more.