Precisely. This is perhaps the strangest part of the original post: C++ has the same performance advantages as Rust! It has them not because it's more safe (although it is, in some regards), but because it allows programmers to express behaviors that the compiler can reason about statically.
Rust's assumption is that it's the compiler's job to reject all wrong programs (usually with a helpful diagnostic). In C++ the assumption is that it's the compiler's job to permit all correct programs.
You obviously ideally want both, but that's not actually possible when you have a language this powerful. So, Rest's choice means sometimes (more rarely these days but it can happen) you will write a program that is correct, but the compiler doesn't believe you and rejects your program, you will need to alter it, perhaps after alterations it's actually nicer, but equally perhaps you feel this made it uglier or slower, nevertheless you have no choice in Rust (well, you could try waiting a few years, the compiler gets smarter)
However the C++ choice means sometimes (maybe even often) you will write a program that isn't correct and the compiler gives you no indication whatsoever that there's a problem, you get an executable or object file or whatever out, but what it does is completely arbitrary. Maybe it works how you expected... until it doesn't.
The magic phrase in the C++ standard is "Ill-formed, no diagnostic required". For example suppose you try to sort some floats in C++ 20. That's ill-formed (floats aren't in fact Totally Ordered but the function signature says you promise they are) and no diagnostic is required for... whatever it is your program now does. Maybe it crashes, maybe it works fine, not their problem, good luck with that.
Now, probably if all your floats are like boring normal finite reals like -2.5 or something this will work fine, there's no practical reason it wouldn't, but who knows, the C++ language denies all responsibility. So it gets to be very "optimal" here since it can do whatever it wants and it's your fault.
To expand on your float sorting example, sorting a slice[1] in Rust requires the element type to implement the Ord trait, i.e. be totally ordered. Trying to sort a slice of floats will result in a compiler error, even though it might be totally fine as long as all your floats are "ordinary".
Instead, to sort a slice of floats, you have to explicitly specify what would happen for the non-ordinary cases; e.g. by using `.sort_by(f32::total_cmp)`, where f32::total_cmp()[2] is one possible interpretation of a total ordering of floats. This requires writing more code even for cases where it would be completely unnecessary.
So rather than introducing a hard to detect bug (with NaN, Inf, -Inf), Rust makes me think about it and not just let whoever worked on compiler decide.
How is this a negative? I'd rather program fail at compile than runtime, and rather it fail loudly than quietly.
Also Rust doesn't prevent you from making optimal ordering, just a tinge more verbose.
I also like this priority in Rust, which constantly makes me wonder why the developers allowed shadowing. It has already caused runtime bugs for me while the compiler didn't even throw a warning about it, and as Rust is otherwise so strict about making possible mistakes like this explicit it's definitely not the first cause I consider when debugging.
While I think shadowing is great for code readability and I've never encountered a bug caused by it, you can always make sure clippy doesn't let you do it by putting a `#![deny(clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated)]` at the top level of your crate.
Like proto I've never had this happen, even though I was initially sceptical until I found myself writing stuff like (real examples more complicated hence decision to break them down)
let geese = something(lots_of_birds).bunch().of_chained().functions();
let geese = geese.somehow().just().count_them(); // We don't actually need geese, just #
Could you name that first variable something else? Yeah. But, it's geese, it's not the number of geese, it's a different type, but it is just geese, that's the right name for it. OK, maybe rename the second variable? But number_of_geese is a stupid variable name, I would push back on a patch which tried to name a variable that because it's stupid. n_geese isn't stupid, but it is ugly and Rust is OK with me just naming it geese, so, geese it is.
However, if you do run into trouble those Clippy rules can save you. You probably will find you don't want them all (or perhaps any of them) at deny, but Rust is content for you to decide you only want a warning (which you can then suppress where appropriate) and importantly these are three rules, you might well decide you only hate shadow_same or shadow_reuse or something. Here's the link specifically for shadow_reuse as an example:
Safety features. The committee are, perhaps unconsciously, biased against safety on the presumption (seen in many comments here on HN) that safer has to mean lower performance.
But part of the impetus for Carbon is that WG21 (the C++ Standards Committee) rejected proposals that C++ should focus on better performance and safety. So maybe performance is no longer important either. What's left?
Where they've taken things which might appear on the surface to be modelled on a safer Rust feature, usually the committee insists they be made unsafe. For example suppose I call a Rust function which might return a char or might not, it returns Option<char> and if I'm an idiot and I try to treat that as a char, it doesn't type check because it isn't one, I need to say what I'm going to do when it isn't or else that won't compile.
You can write that in modern C++... except it can automatically try to take the char (which isn't there) out of the empty optional structure and that's Undefined Behaviour. So whereas the Rust prevents programmers from making easy mistakes, the C++ turns those into unexploded bombs throughout your code.
Many on the C++ committee are interested in the borrow checking, but are not sure how to make it work in C++. The hard part is they cannot break compatibility with code that is legal with previous versions of C++. If there is even one pathological case where the borrow checker will reject code that doesn't have a memory leak then they will not accept it, and require whoever proposes this borrow checker to prove the absence of such a thing. (note if it rejects code that worked until the leaks mean you run out of memory they will accept that). I don't know if such a thing even exists, but if it does I'm confident that in Rust it is new code that you can write differently to avoid the bug, while with C++ that may be a very massive effort to figure out 25 year old code nobody understands anymore before you can rewrite it.
One obvious corner case: It is very common to allocate a buffer at startup and let the system clean it up when the program exits. (often this is embedded cases where the only way for the program to exit is power off). I don't know how you do this in rust (if you can - I'm not a rust expert)
> allocate a buffer at startup and let the system clean it up when the program exits
This is possible with the lazy_static library or (not yet in stable Rust) OnceCell. It allows you to allocate & initialize any datastructure once during runtime and get global read-only access.