Rust fails to prove basic indirection to be statically safe and instead does a run-time check.
fn main() {
let v = vec![1, 2, 3];
v[5];
}Now it's your example that's too simple. I can't present a counter-example that wouldn't look totally different: if you avoid the indexing operator then trivially incorrect code like that becomes unrepresentable. It's like if I said destructors help you avoid memory leaks and you asked how you'd avoid a leak in a single-function program that does nothing but call `Box::leak()`.
Again: I'm not disputing the fact that the rust index operator does a dynamic bounds check.
If this was an array, that is, a built-in type with a static length, this example does give a compile-time error: https://play.rust-lang.org/?version=stable&mode=debug&editio...
However, a vector's length is inherently a runtime concept, and is a user-defined library type, so the compiler does not attempt to directly reason about this at compile time. However, for this example, post-optimizations, it doesn't do a runtime check at all: it calls the panic directly, because the optimizer does in fact reason that this always panics and removes the dynamic check.
It is true that Rust inserts dynamic checks for things that it can't prove statically, but so do dependently typed languages. "Please read an integer from stdin and then load that element of an array" is not possible to statically check, it must rely on runtime checks, definitionally.