It doesn't access any elements out of bounds. It gives you an empty slice - what's between the last element and the end of the original slice/vec.
You don't get an empty slice for later indices, because the empty slice isn't some "null" or "undefined" sentinel value. It's a slice with a length of (original_length - index), which is 0. It doesn't work when the index is greater than the length, because that would be negative.
Being able to point to, but not read, one element past the end is necessary for almost every iteration algorithm, and it's a thing Rust inherited from C.
Alternatively, think of fences and fence posts – you need one more post after the last section of the fence, and that's not an off-by-one error. Slices use the posts to define start/end. Reading of items uses the fence sections between the posts.
in rust you can always index a region of memory of size zero, it's never invalid because by being of size 0 you can't use it to do any harm. because of that making it return [] is simply the easiest to implement safest and most ergonomic option here
I think my non-native english failed me this time. I know what ".." means but for some reason when thinking about slices I reason about "slice indices" (see @quinedot answer) so the length of the slice is a valid "slice index". The addition of the "..=" operator obviously breaks this mental model and that's probably why I never use it.