Rust is certainly not the simplest language you'll run into, but C++ is incredibly baroque, they're not really comparable on this axis.
One difference which is already important and I think will grow only more important over time is that Rust's Editions give it permission to go back and fix things, so it does - where in C++ it's like venturing into a hoarder's home when you trip over things which are abandoned in favour of a newer shinier alternative.
If you're trying to remember what the language is where there's no immediately obvious straightforward way to get the length of an array, it's not Rust or C++; you must have been thinking of C.
In Rust the arrays aren't stunted left over primitive types which weren't gifted modern features, array.len() works because all Rust's types get features like this, not just the latest and greatest stuff.
(C++ arrays are different from arrays in many other programming languages, though not necessarily Rust, in that their type specifies their length, so in a way this is something you already "have" but certainly there are cases where it is convenient to "get" this information from an object.)
Perl is so bad about this that I once worked on a very old codebase in which I could tell approximately when it was written based on which features were being used.
That's one difference. And the other important differences are:
- Rust apps can depend on library "headers" written in other editions. That's the whole deal with editions! Breaking changes are local to your own code and don't fracture the ecosystem.
- Rust has a built-in tool that automatically migrates your code to the next edition while preserving its behavior. In C++, upgrading to the next standard is left as an exercise for the reader (just like everything else). And that's why it's done so rarely and so slowly.
Also it requires everything to be compiled with the same compiler, from source code.
There are tools available in some C++ compilers for migration like clang, note the difference between ISO languages with multiple implementations, and one driven by its reference compiler.
Not sure what you're talking about. Any specific examples?
> Also it requires everything to be compiled with the same compiler, from source code.
It's not related to editions at all. It's related to not having an implicit stable ABI.
It's possible have a dynamic Rust library that exposes a repr(C) interface, compile it into an .so using one version of the compiler, and then compile the dependent "pure Rust" crates using another compiler that's just going to read the metadata ("headers") of that library and link the final binary together. Same as in C and C++. You just can't compile any Rust code into a stable dynamic library, by defalut. (You can still always compile into a dylib that needs a specific compiler version)
You're correct that the possible changes in editions are very limited. But editions don't hinder interoperability in any way. They are designed not to. Today, there are no interoperability problems caused by editions specifically.
> compromises will be required, specially regarding possible incompatible semantic differences across editions.
That's just an assumption in your head. 4 editions later, it still hasn't manifested in any way.
ABIs and multiple versions of the language runtime, are part of what defines a language ecosystem, hence why editions don't really cover as much as people think they do.
We (as in the industry) will eventually get reliable ways to generate applications directly to machine code, just as optimizing compilers took a couple of decades to beat hand written Assembly, and generate reliable optimized code.
So editions, regardless of what they offer, might not be as relevant in such a timeframe from a couple of decades ahead.
What is the compiler supposed to generate if code from edition X calls code in edition X + 10, with a lambda written in X + 5, expecting using a specific language construct that has changed semantics across editions, maybe even more than once?
Everything compiling from source, same compiler.
You have gotten it across just fine.
We're trying to get across that no one is ever going to do "global" incompatible semantic changes in editions. That's been understood from the start. Exactly because of the problems that you describe.
Although, it's still possible to "sneak in" surprisingly useful semantic changes if they are local to the function body: https://doc.rust-lang.org/stable/edition-guide/rust-2024/tem...
Furthermore some programmers really like complicated languages like Rust, Haskell, etc others like straightforward languages like Go, Python, etc.
Python belongs to the complicated languages section, people that think Python is straightforward never bothered reading all the manuals, nor had to put up with all the breaking changes throughout its history, it wasn't only 2 => 3, that was the major event, every release breaks something, even if little.
I am not sure what you mean by this. I write go code pretty much everyday and that code looks vaguely the same as it would do in C#, or Python, or JavaScript.
> Python belongs to the complicated languages section, people that think Python is straightforward never bothered reading all the manuals, nor had to put up with all the breaking changes throughout its history, it wasn't only 2 => 3, that was the major event, every release breaks something, even if little.
I've read the manuals. Most docs appear to be reasonably well written, even if I find many of the examples a bit odd (I've never been a big fan of Monty Python, so the spam and eggs examples are all a bit odd).
The 2 => 3 change was probably a big thing for those migrating all the existing libraries, frameworks. But as someone that uses it for prototyping stuff, I barely noticed the difference.
Python is absolutely not straighforward, it's a huge language with many moving parts and gotchas.
Although, I admit, both are very easy to start programming in and to start shipping broken projects that appear working at first. They are good for learning programming, but terrible for production.
> Rust didn’t really solve memory safety, it just pushed all the complexity into the type system.
Yes, that's what it did, and that's the right tradeoff in most cases. Where the compiler can't make a reasonable default choice, it should force the programmer to make a choice, and then sanity-check it.
> If one was struggling with memory errors in C++ that’s nice. If one was using Java, that still sucks.
It's nice for those struggling with uncaught exceptions, null pointer bugs, mutithreading bugs, and confusing stateful object graphs in Java.
Yes, Golang and Python and Java are very easy to start programming in. And unless we’re dealing with some really complex problem, like next-gen cryptocurrencies ;), by the time the Rust teams have gotten their code working and nice and proper as any Rust code needs to be, the Golang/Python/Java teams have already released to customers.
If one wants to be super-cautios and move accordingly slower to be really really sure they have no memory errors then that’s fine. There’s a market for that stuff. But selling this approach as a general solution is disingenuous.
...have already released a broken prototype that appears to be working for now.
I'm yet to see a case where manually "hardening" your software is faster than writing a "similarly-good" program in Rust. That's just anti-automation. Why repeat the same work in every project and bloat your codebase, when the compiler can carry that burden for you? In my experience, Rust makes you write production-grade software faster than when using other languages.
> But selling this approach as a general solution is disingenuous.
I agree! There are legitimate cases where releasing a broken prototype as quickly as possible is important. There's room for that.
But I agrue that it's not the case for most "serious" production software that would be maintained for any period of time. And that Rust is the preferable option for writing such production software.
Maybe Rust isn't optimized for throwaway projects and that's fine.
But it requires a different mindset which I think many in our industry finds hard to work with. Rust exposes the imperfections rather than hiding them until you explicitly check for them like in most other languages.
Just clone and unwrap liberally and throwaway projects go fast.
Starting something in Rust only makes sense for very few domains and software categories.
But they don’t because the compiler does not carry the burden for you. It puts the burden of writing code in a way that satisfies the Rust lifetime management design on you, and that’s what you’ll be doing forever.
There zero proof that Rust software is higher quality than equivalent e.g. Swift or Java. The memory-safety trick only works against C and C++.
I never said that. I said that it's more appropriate and productive.
> If it were really “preferable for writing such production software”, more people would be using it.
There are many valid reasons for not using it, even in new projects. Such as human preferences, lack of specific libraries, and simply not having time to learn yet another language for a non-10x benefit.
> the compiler does not carry the burden for you. It puts the burden of writing code in a way that satisfies the Rust lifetime management design on you, and that’s what you’ll be doing forever.
It carries a different burden for me. In exchange for the burden that you mention, I don't worry about mutable aliasing bugs (including data races), breaking the code by refactoring it, manually unit-testing basic properties that could be types, debugging non-obvious non-local effects (such as exceptions and null values invisibly propagating through the layers of the app), micro-optimizing the performance manually, and so on. The benefits are known. I've almost forgot how to use a debugger at this point.
It's a tradeoff. I find it more comfortable and less cognitively-taxing to work this way, once the initial learning curve is behind. Less unpredictable problems to distract you from solving the problem. The borrow checker problems largely go away as you get more comfortable with it. And it's a skill that's instantly transferrable between all Rust projects, unlike project-specific defensive practices in other languages (the "manual hardening" needed to get decent quality). It's a long-term investment that automates a small, but not insignificant part of your work.
> There zero proof that Rust software is higher quality than equivalent e.g. Swift or Java.
The Google's director of engineering gave a talk saying that their Rust teams are just as productive as their Go teams, while also enjoying "a decreased defect rate over time": https://www.reddit.com/r/rust/comments/1bpwmud/media_lars_be...
> The memory-safety trick only works against C and C++.
That's absolutely true, and that's an important thing to keep in mind when framing discussions about Rust.
Additionally, given its ML influence, too many people enjoy doing Haskell level FP programming in Rust, which puts off those not yet skilled in the FP arts.
Also the borrow checker is the Rust version of Haskell burrito blogs with monads, it is hard to get how to design with it in mind, and when one gets it, it isn't that easy to explain to others still trying to figure it out.
Hence why from the outside people get this opinion over Rust.
Naturally those of us with experience in compilers, type systems theory and such, see it differently, we are at another level of understanding.
Eh. Haskell monads are a math-centric way of performing operations on wrapped types (stuff inside a monad). Rust borrow checker is way more pragmatic-centric, i.e. how can we prevent certain behaviors.
The difference being you don't see Monads being replaced by Tree-Monads, without impacting the code.
> and when one gets it, it isn't that easy to explain to others still trying to figure it out.
So is going from 0-based to X-based arrays (where X is an integer); Or learning a new keyboard layout. Just because it's hard (unfamiliar) doesn't mean it's impossible.
> Naturally those of us with experience in compilers, type systems theory and such, see it differently, we are at another level of understanding.
I mean yeah, you might have a new perspective, but so can it be a blinder to how people learn language. Curse of knowledge, and all that.
I am a fan of Rust but it’s definitely a terse language.
However there are definitely signs that they have thought about making it as readable as possible (by omitting implicit things unless they’re overwritten, like lifetimes).
I’m reminded also about a passage in a programming book I once read about “the right level of abstraction”. The best level of abstraction is the one that cuts to the meat of your problem the quickest - spending a significant amount of time rebuilding the same abstractions over and over (which, is unfortunately often the case in C/C++) is not actually more simple, even if the language specifications themselves are simpler.
C codebases in particular, to me, are nearly inscrutable unless I spend a good amount of time unpicking the layers of abstractions that people need to write to make something functional.
I still agree that Rust is a complex language, but I think that largely just means it’s frontloading. a lot of the understanding about certain abstractions.
Adding features in particular is a breeze and automatically the compiler/language will track for you the places that use only old set of traits.
Tooling is still newer though and needs polish. Generic handling is interesting at times and there are related missing features for that in the language, vis a vis specializations in particular.
Basic concurrency handling is also quite different in Rust than other languages, but thus usually safer.
But it does! To qoute my top-level comment:
> What about about race conditions, null pointers indirectly propagated into functions that don't expect null, aliased pointers indirectly propagated into `restrict` functions, and the other non-local UB causes?
In other words: you set some pointer to NULL, this is OK in that part of your program, but then the value travels across layers, you've skipped a NULL check somewhere in one of those layers, NULL crosses that boundary and causes UB in a function that doesn't expect NULL. And then that UB itself also manifests in weird non-local effects!
Rust fixes this by making nullability (and many other things, such as thread-safety) an explicit type property that's visible and force-checked on every layer.
Although, I agree that things like macros and trait resolution ("overloading") can be sometimes hard to reason about. But this is offset by the fact that they are still deterministic and knowable (albeit complex)
> in fact it helps because it allows compilers to turn it a trap without requiring it on weak platforms
The "shared xor mutable" rule in Rust also helps the compiler a lot. It basically allows it to automatically insert `restrict` everywhere. The resulting IR is easier to auto-vectorize and you don't need to micro-optimize so often (although, sometimes you do when it comes to eliminating bound checks or stack copies)
> Restrict is certainly dangerous, but also rarely used and a clear warning sign, compare it to "unsafe".
It's NOT a clear warning sign, compared to `unsafe`. To call an unsafe function, the caller needs to explicitly enter an `unsafe` block. But calling a `restrict` function looks just like any normal function call. It's easy to miss an a code review or when upgrading the library that provides the function. That the problem with C and C++, really. The `unsafe` distinction is too useful to omit.
That's exactly because it's too dangerous and the developers quickly learn to avoid it instead of using it where appropriate! Same with multithreading. C leaves a lot of optimization on the table by making the available tools too dangerous and forcing people to avoid them altogether.
That's how you get stuff like memory-safe Rust PNG decoders being 1.5x faster than established C alternatives that had much more effort put into them (https://www.reddit.com/r/rust/comments/1ha7uyi/memorysafe_pn...). Or the first parallel CSS engine being written in Rust after numerous failed attempts in C++ (https://www.reddit.com/r/rust/comments/7dczj9/can_stylo_be_i...). Read those threads in full, there are some good explanations there.
> you need to exaggerate the practical problems in C
I thought, the famous "70% of vulnerabilities" report settled this once and for all.
Care to elaborate why?
> And cherry picking individual benchmarks too.
Do you have any general, comprehensive benchmarks or statistics that would indicate the opposite? I would include one if I had one at hand, because that would be a stronger argument! But I'm not aware of such benchmarks. I have to cherry pick individual projects. I don't want to.
I still claim that, as a general trend, Rust replacements are faster while also being less bug-prone and taking much less time to write. Another such example is ripgrep.
C can't match that. In C, you're basically acting as a human compiler, writing lots of code that could be generated if you used a more expressive language. Plus, as has been mentioned, it supports refactoring easily and safely better than any language outside of the Haskell/ML space.
The advantages of Rust are a package which includes safety, expressiveness, refactoring support. You don't need to exaggerate anything for that package to make sense.
The fact that you, as an individual, prefer to use an unsafe, weakly typed language isn't very relevant to that.
The issue is not that it's not possible to write secure programs in C, the issue is that in practice, on average, people don't.
Pushing the use of memory-safe languages will reduce the number of security vulnerabilities in the entire software ecosystem.
This is a line of thinking I used to see commonly when dynamic typing was all the rage. I think the difference comes from people who view primarily work on projects where they are the sole engineer vs ones where they work n+1 other engineers.
"just add assertions" only works if you can also sit on the shoulder of everyone else who is touching the code, otherwise all it takes is for someone to come back from vacation, missing the refactor, to push some code that causes a NULL pointer dereference in an esoteric branch in a month. I'd rather the compiler just catch it.
Furthermore, expressive type systems are about communcation. The contracts between functions. Your CPU doesn't case about types - types are for humans. IMO you have simply moved the complexity from the language into my brain.
You mean obsolete and subtly wrong comments buried among preprocessor directives in some far-upstream header files?
This happens a lot in discussion about programming complexity. What you are doing is changing the original problem to a much simpler one.
Consider a parsing function parse(string) -> Option<Object>
This is the original problem, "Write a parsing function that may or may not return Object"
What a lot of people do is they sidetrack this problem and solve a much "simpler problem". They instead write parse(string) -> Object
Which "appears" to be simpler but when you probe further, they handwave the "Option" part to just, "well it just crashes and die".
This is the same problem with exceptions, a function "appears" to be simple: parse(string) -> Object but you don't see the myriads of exceptions that will get thrown by the function.
But in the end, you can write Option just fine it C. I agree though that C sometimes can not express things perfectly in the type system. But I do not agree that this is crucial for solving these problems. And then, also Rust can not express everything in the type system. (And finally, there are things C can express but Rust can't).
No, you can't. In the sense that the compiler doesn't have exhaustiveness checks and can't stop you from accessing the wrong variant of a union. An Option in C would be the same as manually written documentation that doesn't guarantee anything.
std::optional in C++ is the same too. Used operator* or operator-> on a null value? Too bad, instant UB for you. It's laughably bad, given that C++ has tools to express tagged unions in a more reliable way.
> And then, also Rust can not express everything in the type system. (And finally, there are things C can express but Rust can't).
That's true, but nobody claims otherwise. It's just that, in practice, checked tagged unions are a single simple feature that allows you to express most things that you care about. There's no excuse for not having those in a modern language.
And part of the problem is that tagged unions are very hard to retrofit into legacy languages that have null, exceptions, uninitialized memory, and so on. And wouldn't provide the full benefit even if they could be retrofitted without changing these things.
The solution have to scale linearly with the problem at hand, that is what it means to have a good solution.
I agree with the article that Rust is overkill for most use cases. For most projects, just use a GC and be done with it.
> But I do not agree that this is crucial for solving these problems. And then, also Rust can not express everything in the type system.
This can be taken as a feature. For example, is there a good reason this is representable?
struct S s = 10;
I LOVE the fact that Rust does not let me get away with half-ass things. Of course, this is just a preference. Half of my coding time is writing one-off Python scripts for analysis and data processing, I would not want to write those in Rust.
> But in the end, you can write Option just fine it C.
Even this question have a deeper question underneath it. What do you mean by "just fine"? Because to me, tagged enums or NULL is NOT the same thing as algebraic data types.
This is like saying floating points are just fine for me for integer calculations. Maybe, maybe for you its fine to use floating points to calculate pointers, but for others it is not.
You won't understand it unless refactor some Rust programs.
Bunny summed it up rather well. He said in most languages, when pull on some thread, you end disappearing into a knot and you're changes are just creating a bigger knot. In Rust, when pull on a thread, the language tells you where it leads. Creating a bigger knot generally leads to compile errors.
Actually he didn't say that, but I can't find the quote. I hope it was something like that. Nonetheless he was 100% spot on. That complexity you bemoan about the language is certainly there - but it's different to what you have experienced before.
In most languages, complex features tend lead to complex code. That's what made me give up on Python in the end. When you start learning Python, it seems a delightfully simple yet powerful language. But then you discover metaclasses, monkey patching, and decorators, which all seem like powerful and useful tools, and you use them to do cool things. I twisted Python's syntax into grammar productions, so you could write normal looking python code that got turned into an LR(1) parser for example. Then you discover other peoples code that uses those features to produce some other cute syntax, and it has a bug, and when you look closely your brain explodes.
As you say C doesn't have that problem, because it's such a simple language. C++ does have that problem, because it's a very complex language. I'm guessing you are making deduction from those two examples that complex languages lead to hard to understand code. But Rust is the counter example. Rust's complexity is forces you to write simple code. Turns out it's the complexity of the code that matters, not the complexity of the language.
In C, you can make partial changes and accept a temporary inconsistency. This gives you a lot of flexibility that I find helpful.
Yes it limits refactoring. But all languages provide syntactic and semantic constraints you must operate within. You can't just add line noise to a C program and expect it to compile.
So your complaint isn't that there are limits, it's that there is less of them in C, so it's easier to make changes in C without putting too much thought into it. That is absolutely correct. It's also true that's is far easier to introduce a bug in C code when you refactor than it is in Rust, and that's so because it's harder to write buggy code in Rust that gets past the compiler.
Perhaps an example. Consider:
inline char foo(uint8_t i, char a[]) { return *(a + 1); }
What does this code do? In C or C++, it's impossible to answer. If i overflows, it's UB. If you go past the end of the array, it's UB. Since it's inlined, there is no way to know if either could happen. If compiler can prove some particular instance is UB, it can do whatever it damned well pleases, without informing the poor programmer. There are lots of worse examples, particularly in C++.In Rust, it's entirely predictable what happens for any given input, as Rust forbids UB in safe code. But in order to pull that off, Rust forces you to re-write the above function, perhaps into something like this:
fn foo(i: u8, a: &[u8]) -> u8 { a[(i + 1) as usize] }
or: fn foo(i: u8, a: &str) -> char { a.chars().skip((i as usize) + 1).next().unwrap() }
or many other variations depending what you actually need to do. Notice for example you where forced to say whether you're happy with incrementing 255 to 256. If you weren't, you would write it as (i + 1) as usize.Yes, it's more mental effort. In return, you don't get your arse handed to you on a platter because you recompiled with a newer, smarter version of the compiler that noticed you violated some language rule only a language lawyer would know, and took advantage of it.
Then we have the functions that might be re-entrant or not, in the presence of signals, threads,...
But you're willing to write many comments complaining that Rust is hard to refactor. Rust is the easiest language to refactor with I've ever worked in, and I've used a couple dozen or so. When you want to change something, you change it, and then fix compiler errors until it stops complaining. Then you run it, and it works the first time you run it. It's an incredible experience to worry so little about unknown side-effects.
Their refactoring comments look focused on C versus C++ to me, with a bit of guessing Rust is like C++ in a way that is clearly labeled as speculation.
So I don't see the problem with anything they said about refactoring.
Oh? So how do you refactor a closure into a named function in Rust?
I have found this to be of the most common failure modes that makes people want to punch the monitor.
(Context: in almost all programming languages, a closure is practically equivalent to an unnamed function--refactoring a closure to a named function tends to be pretty straightforward. This isn't true for Rust--closures pick up variable lifetime information that can be excruciatingly difficult to unwind to a named function.)
Both the LLM and humans doing it are relying on the fabulous work the rust compiler team has done in generating error messages with clear instructions on how to fix problems.
Really? How confident are you to change a data structure that uses an array with linear search lookup to a dictionary? Or a pointer that now is nullable (or is now never null)?
Unless you have rigorous test or the code is something trivial, this would be a project of its own.
I am pretty sure I can swap out the implementation of the dictionary in the rust compiler and by the time the compilation issues are worked out, the code would be correct by the end of it (even before running the tests)
Refactoring Rust projects is clearly the easiest because the compiler and type system ensure the program is correct at least in terms of memory access and shared resource access. It doesn't protect me from memory leaks and logical errors. But since Rust has a built-in testing framework, it's quite easy to prepare tests for logical errors before refactoring.
C/C++ refactoring is a nightmare - especially in older projects without modern smart pointers. Every change in ownership or object lifetime is a potential disaster. In multi-threaded applications it's even worse - race conditions and use-after-free bugs only manifest at runtime, often only in production. You have to rely on external tools like Valgrind or AddressSanitizer.
Python has the opposite problem - too much flexibility. You can refactor an entire class, run tests, everything passes, but then in production you discover that some code was dynamically accessing an attribute you renamed. Type hints help, but they're not enforced at runtime.
Rust forces you to solve all these problems at compile time. When you change a lifetime or ownership, the compiler tells you exactly where you need to fix it. This is especially noticeable in async code - in C++ you can easily create a dangling reference to a stack variable that an async function uses. In Rust, it simply won't compile.
The only thing where Rust lags a bit is compilation speed during large refactors. But that's a small price to pay for the certainty that your code is memory-safe.
Another area where Rust absolutely excels is when using AI agents like Claude Code. It seems to me that LLMs can work excellently with Rust programs, and thanks to the support of the type system and compiler, you can get to functional code quickly. For example, Claude Code can analyze Rust programs very well and generate documentation and tests.
I think Rust with an AI agent has the following advantages:
Explicit contract - the type system enforces clear function interfaces. The AI agent knows exactly what a function expects and what it returns.
Compiler as collaborator - when AI generates code with an error, it gets a specific error message with the exact location and often a suggested solution. This creates an efficient feedback loop.
Ownership is explicit - AI doesn't have to guess who owns data and how long it lives. In C++ you often need to know project conventions ("here we return a raw pointer, but the caller must not deallocate it").
Fewer implicit assumptions - in Python, AI can generate code that works for specific input but fails on another type. Rust catches these cases at compile time.
(1) The "intimidating syntax". Hey, you do not even need to be using <$> never mind the rest of those operators. Perl and Haskell can be baroque, but stay away from that part of the language until it is useful.
(2) "Changes are not localized". I'm not sure what this means. Haskell's use of functions is very similar to other languages. I would instead suggest referring to the difficulty of predicting the (time|space) complexity due to the default lazy evaluation.
FTA:
> In contrast, Haskell is not a simple language. The non-simplicity is at play both in the language itself, as evidenced by its intimidating syntax, but also in the source code artifacts written in it. Changes are not localized, the entire Haskell program is one whole — a giant equation that will spit out the answer you want, unlike a C program which is asked to plod there step by step.
Edited to make the critique more objective.
But you have to be able to read and understand code using them.
Anothere way of putting it is, if you didn't care about backwards-compatibility, you could greatly simplify C++ without losing anything. You can't say the same about Rust; the complexity of Rust is high-entropy, C++'s is low-entropy.
For Rust, I think it is a bit of a different story and it is harder to point to specific features. The language is clearly much better designed (because it was more designed and did not evolve so much) and because of its roots in functional programming. Just overall, the complexity is too high in my opinion and it a bit too idealistic and not pragmatic enough.
This is an ideal programming language for certain types of people. It also gives the programming language certain properties that make it useful when provable correctness is a concern (see Ferrocene).
I haven't been following their work, though. It seems they are working on stacked borrows.
We're a few years past that, there's now 'tree borrows' which just had its paper published: https://www.ralfj.de/blog/2025/07/07/tree-borrows-paper.html
They didn’t do the best with what they had. Sure, some problems were caused by C backwards compatibility.
But so much of the complexity and silliness of the language was invented by the committee themselves.
Those folks eventually move to something else and adopt "C++ the good parts" instead.
Rust is a dead simple language in comparison.
Rust doesn’t have a standard, it has a book, so you should refer to the initialization section from Stroustrup’s C++ book to keep things fair.
There are other compilers in development, and they're able to coordinate with these documents. There is of course always more work to do, but it's really not as far away as some people believe.
US corporations have massive influence over Rust since they bought seats on the board. Speaking of the board, it seems to me it’s all US based.
The company working on the official spec is a Berlin LLC.
US corporations also have massive influence on the C and C++ specifications, just look at the brouhaha around Bloomberg and contracts for C++, for example.
And the Rust Foundation does not author the specification, the Rust Project does. So in many ways, companies on the Foundation board have less direct influence than the companies who send their employees as representative to WG21 or similar.
https://isocpp.org/wiki/faq/wg21 is still very US skewed, but much more diverse than both https://www.rust-lang.org/governance/teams/lang and and the foundation. The latter does influence the direction of the language: https://rustfoundation.org/safety-critical-rust-consortium/
And sure, corporate influence is worth talking about, but the point is C++ isn’t free of it either.
The big weak spot really is lack of community outside of Apple platforms.
Having just ported a small library from Rust to Swift, it’s fairly clear that Rust is ridiculously fast but Swift is just so much more readable. Much easier to debug too.
Also - why would Rust only have a max-heap? In the library I ported, the authors had to reverse the algorithmic logic in the entire library for this.
Even though it’s only about half as fast as Rust, Swift performance does start to shine with concurrency.
All you have to do is use a small wrapper type and you get a min heap https://doc.rust-lang.org/std/collections/struct.BinaryHeap....
In Rust, for most users, the main source of complexity is struggling with the borrow checker, especially because you're likely to go through a phase where you're yelling at the borrow checker for complaining that your code violates lifetime rules when it clearly doesn't (only to work it out yourself and realize that, in fact, the compiler was right and you were wrong) [1]. Beyond this, the main issues I run into are Rust's auto-Deref seeming to kick in somewhat at random making me unsure of where I need to be explicit (but at least the error messages basically always tell you what the right answer is when you get it wrong) and to a much lesser degree issues around getting dyn traits working correctly.
By contrast C++ has just so much weird stuff. There's three or four subtly different kinds of initialization going on, and three or four subtly different kinds of type inference going on. You get things like `friend X;` and `friend class X;` having different meanings. Move semantics via rvalue references are clearly bolted on after the fact, and it's somewhat hard to reason about the right things to do. It has things like most-vexing parse. Understanding C++ better doesn't give you more confidence that things are correct; it gives you more trepidation as you know better how things can go awry.
[1] And the commonality of people going through this phase makes me skeptical of people who argue that you don't need the compiler bonking you on the head because the rules are easy to follow.
† This phrase would have been idiomatic many years ago but it is still used with the same intent today even though its meaning is no longer obvious, the idea is that a farmer at market told you this sack you can't see inside ("poke") has a piglet in it, so you purchase the item for a good price, but it turns out there was only a kitten in the bag, which (compared to the piglet) is worthless.
Move by default is the thing which complicates Rust so much.
Fair, but this relative. C++ has 50 years of baggage it needs to support--and IMO the real complexity of C++ isn't the language, it's the ecosystem around it.