To clarify: The handrail is already there
It's really not. Or if we want to continue the analogy, the handrail is there, but it has gaps through which you can still fall. It's your responsibility to know where the gaps are and to grasp the handrail in the right places -- and many of the gaps are subtle.
I've been writing C++ for 35 years, and have been a huge fan of Modern C++ since its introduction. The combination of RAII and move semantics is incredibly powerful and represents an enormous advance in efficient memory safety. But Rust takes all of the safety-related ideas that C++ has and significantly raises the bar. Not only does Rust actively discourage you from using unsafe practices, unlike C++ which requires you to actively choose to use the safer ones, Rust's borrow checker goes far beyond what any C++ compiler can do to diagnose subtle mistakes that could lead to memory errors, at zero runtime cost (though compilation is slower).
Here's an example:
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin();
vec.push_back(6);
std::cout << *it << std::endl;
Obviously, you should not use vector iterators after you've made a change to the vector that might cause a reallocation (or, equivalently, grab a reference or pointer to an element of a vector and then use it after doing something that might cause a reallocation). But the point is that you have to know and remember this rule, along with a lot of other rules about what you should and shouldn't do. Further, in more complex code it can get really hard to tell if you're following the rules or not (which is a hint that you should simplify the code, but that's a separate issue). Further complicating the issue is that the above code might work "correctly" most of the time, because it only fails when push_back reallocates the vector. Also, when it fails, it may still appear to work most of the time, because the reallocation might not have changed the values in the referenced memory, and the iterator might still be able to find the "right" value even though it's getting it from unallocated heap storage. This makes for intermittent heisenbugs that can be very hard to find.
In Rust, you can't do this sort of thing. The compiler won't let you. In the equivalent Rust code, getting the iterator would take an immutable reference to the vector, and then trying to call push_back would require also taking a mutable reference, but the borrow checker won't let you take a mutable reference if there's already another reference. Note that the borrow checker's conservatism means that it also often calls out code that is actually fine. One common example is taking mutable references to different parts of a struct. So you occasionally have to do a little extra work (which gets optimized away in every case I've examined, so it doesn't often have a runtime cost) to work around the borrow checker which can be annoying. But unless you use unsafe you know you can't make this sort of error.
In addition, Rust also takes on concurrency errors, providing deep compiler and library support for safe concurrency, which is something C++ doesn't address at all. Rust doesn't fully address the challenge of safe concurrency, unfortunately, because deadlocks and livelocks are still possible, but it makes unsafe concurrent memory accesses just as impossible as it makes other memory errors.
Further, in most areas (not all), Rust has better ergonomics than C++, which makes it more productive and -- IMO -- more fun to use. I still like C++, and when I use it I use the Modern C++ style, structuring my code very similarly to how I'd write it in Rust. But I still use valgrind on the resulting C++ binaries to check for memory bugs, because they are still possible, mostly through subtle reference aliasing or integer over/underflow, and I'm never as confident of the correctness of the result as I am with Rust.