Zig may be a better option when it comes to compile-time evaluation.
Rust is making good progress despite these issues but it's still a pain to use in anything but the most common systems. As soon as you're using SoCs that have an FPGA part, for example, you're forced to use proprietary vendor tools and good luck getting Rust to work with those in the next few decades...
Starting a job in a week which is embedded Rust, but more on the Linux-on-SoC side, we'll see it goes.
The bigger issue is that you are at the whims of llvm as to what your codegen looks like. Regressions as llvm versions are upgraded are routine and can sometimes result in significant regressions on micro benchmarks. The good news is the rust team treats these pretty seriously even if they do take time (and several versions) to get fixed but you’ll never see the issue closed out in the issue tracker. All of performance regressions, codegen bloat regressions, and llvm optimization failures (eg not stripping out a panic handler in safe rust when it should be possible to deduce that the panic cannot be reached) are all tagged and monitored and tests are often added to catch regressions once fixes land. The good news is that when you run into these in safe rust you can usually work around them with unsafe rust - unreachable_unchecked() is your best friend here and is amazingly easy to use either with an if or with a match to disavow a potential state before the code that handles it.
I believe this misconception on your part stems from a lack of awareness (or understanding) of C++ design goals as a language.
Have a look at this writeup: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p21... (caveat: not formally adopted)
constexpr supports the following goals:
* Provide the programmer control over every aspect of performance: Compile-time vs run-time execution is an aspect of performance.
* Excellent ergonomic: Using constexpr/consteval improves on earlier mechanisms for achieving compile-time evaluation, such as macros, template meta-programming etc. Those are unwieldy, often difficult to read, and inflexible (without jumping through hoops). constexpr is not.
etc. etc.
and marginally supports the following goals:
* Code should perform predictably: Control over what exactly runs at run-time improves predictability.
* Leave no room for a lower level language: While constexpr is neither low nor high as a language feature, one can argue that having it undercuts the potential for, say, a "C + constexpr"-like language, or another lower-level language with a more elaborate macro system.
etc. etc.
I could go on naming goals constexpr supports, there are lots more. I challenge you to find goals from which it detracts.
Not only was P2137 "not formally adopted" it was purposefully written so that the committee could say explicitly "No" to these goals. It's in some sense a manifesto for one of the 2022 "C++ successor" languages, Carbon not for C++.
If you want to cite actual goals, you won't get much from WG21, which prefers not to get tied down by having any principles to speak of. Bjarne himself however wrote a book in 1994 "Design & Evolution of C++" which might help you determine some goals.
I don't see constexpr functions in particular as a success. There are too many caveats, it's easy to find C++ programmers who've written what they assume is a constant evaluated expression but it isn't, for one of many reasons their compiler has decided it's runtime evaluated, and the programmer was surprised.
As for constexpr - you only get ambiguity if you assign a constexpr expression/function call to a non-constexpr value. If you use constexpr variables to hold the results of your computation, compile-time evaluation is guaranteed. I don't think it's that confusing.
In practice you do get guaranteed evaluation wherever the result is required at compile time (for example as a non-type template parameter or as an array size).
constexpr, amongst other uses, cleans up code that was hard-to-understand templated code.
Static assert cleans up other complicated constructs.
Make_unique together with heaps of other primitives tries to clean up cumbersome memory management.
[[fallthrough]] tries to solve the fact that break is easily forgotten, but this time not forgotten, but explicitly not there...
You can name so many of new constructs that solve an earlier problem.
If you went through all the cpp standards upgrades, and knew the history, you understand why all these improvements exist. If not, it all looks overly complicated, which in fact, if you look at it objectively, it is.
Executables from Nim also tend to stay pretty lightweight even when using generics / templates.
What is fucked up is us not being perfect machines and not producing ideal design from the first try and then having to deal with compatibility. Well duh.