As a short summary, I'm accessing parts of a vector by slicing it.
The example below lists a stripped down version of the part of the code that got me confused.
Why does the second to last line not panic at all? The vector is of length 2 and I'm going to slice it by starting with the index 2 which should be out of bounds?!
If the second to last line is valid, why does the last line after all behaves differently by panicking?
fn main() {
let foo = vec!["ABC", "DEF"];
println!("{:?}", &foo[0..]); // ["ABC", "DEF"]
println!("{:?}", &foo[1..]); // ["DEF"]
println!("{:?}", &foo[2..]); // [] -> Wtf?!
println!("{:?}", &foo[3..]); // panic: range start index 3 out of range for slice of length 2
}
This is just consistency. For a vector with 2 elements, half-inclusive ranges with an upper bound of 2 are valid. This set includes the empty range [2, 2).
On a lower level, this is also a reflection of how pointer provenence generally works: You're allowed to point at the address that immediately follows you. Otherwise you couldn't do things like iterating a slice by adding to a pointer until it's equal to the address immediately following the slice.
let all = &foo[0..];
let (ptr, len) = (all as *const _ as *const &str, all.len());
println!("All: {ptr:p} {len}");
let tail = &foo[2..];
let (tptr, tlen) = (tail as *const _ as *const &str, tail.len());
println!("2..: {tptr:p} {tlen}");
assert_eq!(tlen, 0);
assert_eq!(ptr.wrapping_add(2), tptr);
You can't (safely) read any out of bounds memory from it, because you can't (safely) read any memory from it: it points to a span of memory covering 0 bytes.