Confusing behaviour about slice index

I'm new to rust, and I got confused about index checking of slice:
let data = vec![0,1];
let slice = &data[2..];
println!("{:?}", slice);

give out result , no panic at all, this is absolutely OOB ref, why this doesn't panic? It's historical problem or just something of design?

when changing to:
let slice = &data[3..];
Program crashed as expected, why OOB of 1 element is so special?

You can think of data[i..] in this case to be short for data[i..2], and think of those indices indicating the places between the elements like so:

0   1   2
+---+---+
| 0 | 1 | 
+---+---+

Then &data[2..] == &data[2..2] is the empty slice just beyond the last element.

A pointer just beyond your allocation still being valid does have a history (predating Rust) around it.

3 Likes

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.

3 Likes

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

That's not correct. As OP explained, &data[3..] does cause a panic, even though the slice 3..2 has also length of zero.

1 Like

I understand that the problem is not the size of the slice but the indices. In this case 2 is a valid slice index while 3 isn't.

1 Like

Ranges defined using .. are not inclusive.

0 .. slice.len() range is the same as an inclusive 0 ..= slice.len()-1 (note the =).

Em... Yes?

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.