The reason it's the way it is right now is that you're thinking about the values, but the compiles works in terms of types.
1..3
and 1..4
both have type Range<usize>
, so they both return the same type from indexing: &[_]
. That's handy in that it means things don't stop compiling if you move the range to be a parameter, for example. You'll see this in various places in Rust, like how if false
still gets its block checked, since the compiler just looks at its bool
ness, and similarly in the difference between loop { … }
and while true { … }
. This is handy for debugging -- you don't get drastically different behaviour just because you made something a literal for a bit.
But it's certainly true that there's a need for something nicer here. What that should look like is still an open question, though, I think. For example, maybe instead of s[1..4]
it's s[1..].first_chunk::<3>()
(https://github.com/rust-lang/rust/pull/95198), where it's a const generic parameter -- and thus in the type system -- instead of a runtime value.