> obviously not ranges, only to return values indicating the range is malformed?
It's not the case. The only think affected by range being generic is that `contains` takes a reference instead of a copy (which btw. can likely be eliminated by the optimizer). Which is necessary to allow thinks like `Range<BigNum>`.
All other things have nothing to do with it being generic but with for which use cases it was designed for.
In the end in rust a Range is mainly an iterator.
If it's a Range<usize> and only then you can also use it to get slice arrays/vectors/slices.
Which means that e.g. the unstable experimental `get_unchecked` function is actually very well defined.
Lastly the reason why you can't enforce `start <= end` is because that would make the creation of an range fallible which would be a horrible usability nightmare, a thing the author somehow misses completely.
The thing is indexing a slice already can panic so moving the panic there is generally a good idea. Similar you always want to have a non-panic path. Which would be e.g. `[T]::get()` which in case of a "bad" slice does the same as on a "bad" index it returns `None`.
In the end both `Range` and `RangeInclusive` are compromises focused on the most common use cases of range, which is a ad-hoc creation "just around" the place you consume it for iteration or slicing of slices. Which also means that e.g. the fact that `RangeInclusive` is bigger is no problem as at the place it's used you elsewise would need to either turn it into a iterator just like `RangeInclusive` adding even more overhead then the current `RangeInclusive`. Sure if you want to store a lot of `RangInclusive`s then this is not the use-case it was defined for and you are better of defining your own range inclusive.
But shouldn't len() panic instead of returning 0? I don't even understand how it could return 0 without having already done all the work to determine it should have returned a negative number.
It's not the case. The only think affected by range being generic is that `contains` takes a reference instead of a copy (which btw. can likely be eliminated by the optimizer). Which is necessary to allow thinks like `Range<BigNum>`.
All other things have nothing to do with it being generic but with for which use cases it was designed for.
In the end in rust a Range is mainly an iterator.
If it's a Range<usize> and only then you can also use it to get slice arrays/vectors/slices.
Which means that e.g. the unstable experimental `get_unchecked` function is actually very well defined.
Lastly the reason why you can't enforce `start <= end` is because that would make the creation of an range fallible which would be a horrible usability nightmare, a thing the author somehow misses completely.
The thing is indexing a slice already can panic so moving the panic there is generally a good idea. Similar you always want to have a non-panic path. Which would be e.g. `[T]::get()` which in case of a "bad" slice does the same as on a "bad" index it returns `None`.
In the end both `Range` and `RangeInclusive` are compromises focused on the most common use cases of range, which is a ad-hoc creation "just around" the place you consume it for iteration or slicing of slices. Which also means that e.g. the fact that `RangeInclusive` is bigger is no problem as at the place it's used you elsewise would need to either turn it into a iterator just like `RangeInclusive` adding even more overhead then the current `RangeInclusive`. Sure if you want to store a lot of `RangInclusive`s then this is not the use-case it was defined for and you are better of defining your own range inclusive.