WG21 (the C++ Standards Committee) feels that since idiomatic C++ was unsafe (e.g. array operations don't have bounds checks) it is consistent for new C++ features to also be unsafe by default, even if the whole point of those features in other languages is to provide an idiomatic safe way to do things.
So for example in Rust you have a native slice type reflecting a dynamically sized view into a contiguous sequence, and of course it's safe -- five[20] will either refuse to compile if the compiler can see this slice is too short, or it will panic at runtime. In C++ as others have mentioned actually std::span just isn't safe when used ergonomically. five[20] in C++ is Undefined Behaviour. std::optional has a safe interface for a Maybe / Option type, but is presented with a more ergonomic unsafe behaviour and that's what people use.
This is not an old-fashioned thing the C++ Committee grew out of, std::expected is in C++ 23, that's a Result type and it likewise offers an ergonomic unsafe API. The safe APIs were seen as a nice-to-have which could miss the train because who needs safety anyway? Just don't make any mistakes.
Tragically, what I would call idiomatic C++ with compiler provided frameworks pre-ISO C++98, made use of bounds checking in their collection classes, and then the C++ Committee went completly to the other way.
Naturally we have now ways to enable them on all major compilers, but many still don't.
The crucial trick in Rust is not that the index operations are bounds checked, that's easy, as you observed plenty of C++ toolchains can do that.
The crucial trick is providing the unchecked operations (with improved performance) as unergonomic alternatives. *(five.get_unchecked_mut(20)) = k; // looks horrible. Nobody wants to write that, so when they don't need it they won't write it. The fact calling get_unchecked_mut requires unsafe is part of how Rust could achieve its goals, but the choice to not make this ergonomic is why it actually delivers in practice.
What CppFront wants here, and P2687 proposes, and lots of other C++ work has suggested, is roughly:
[[suppress(bounds_check)]] { five[20] = k; }
Thus imitating what they think Rust does here, rather than what it actually did, and in the process completely missing the point.
Given that on some domains, there is no way around C or C++, unless one wants to be part of building the ecosystem, I was having big hopes on the clang and VC++ static analysis work for those kind of scenarios.
I can't speak for clang, but in what concerns VC++ is mostly useless still, unless one wants to annotate everything with those kind of annotations + SAL, and even then it is only half way there.
So for example in Rust you have a native slice type reflecting a dynamically sized view into a contiguous sequence, and of course it's safe -- five[20] will either refuse to compile if the compiler can see this slice is too short, or it will panic at runtime. In C++ as others have mentioned actually std::span just isn't safe when used ergonomically. five[20] in C++ is Undefined Behaviour. std::optional has a safe interface for a Maybe / Option type, but is presented with a more ergonomic unsafe behaviour and that's what people use.
This is not an old-fashioned thing the C++ Committee grew out of, std::expected is in C++ 23, that's a Result type and it likewise offers an ergonomic unsafe API. The safe APIs were seen as a nice-to-have which could miss the train because who needs safety anyway? Just don't make any mistakes.