There's lots of things beyond bounds checks that constitute UB and bounds checks are but 1 small item. Generally Rust avoids the runtime cost of that through a three prong strategy of idiomatic Rust amortizing the cost of a bounds check to 0 (e.g. efficient iterator implementations that don't need bounds checks), eliding the check altogether if the compiler can prove it's duplicate / unneeded for some reason, or providing unsafe Rust as an escape hatch when you absolutely need it. C++ already has something similar via `.at` vs `[]` although the shorter notation being the unsafer but faster option is debatable & likely the thing most people use by default. Explicit annotations are probably the better approach.
It's also got a great ecosystem. I love the assume crate to do these annotations instead of writing unsafe code explicitly:
assume!(unsafe: i < v.len())
Now you've explicitly written an assumption that will cause the compiler to elide the bounds check in release mode but still assert it in debug mode (vs just doing unsafe & using variants that bypass the bounds check).