> Zig for ( 0..9 ) |i| { }
> C for (i = 0; i < 9; i++) { }
I know an open interval [0..9) makes sense in many cases, but it's counterintuitive and I often forget whether it includes the last value or not. It's the same for python's range(0, 9).
The better solution to forgetting whether an interval is closed or half-open is to always use only half-open intervals, without any exceptions.
In most cases half-open intervals result in the simplest program, so I agree with the choice of Zig, which is inherited from other languages well-designed from this point of view, e.g. Icon.
I find half-open intervals more intuitive than either closed intervals or open intervals, and much less prone to errors, for various reasons, e.g. the size of a half-open interval is equal to the difference between its limits, unlike for closed intervals or open intervals. Also when accessing the points in the interval backwards or circularly, there are simplifications in comparison with closed intervals.
By "..._MAX" I assume that you mean the maximum value of a given integer type.
In a language where half-open intervals are supported consistently in all the places, this would be solved trivially, e.g. for a signed byte the _MIN and the _MAX values would be defined as -128 and +128, more intuitively than when using closed intervals, where you must remember to subtract 1 from the negated minimum value.
Even the C language has some support for half-open intervals, because the index pointing after the last element of an array is a valid index value, not an out-of-range value (though obviously, attempting to access the array through that index value would be trapped as an out-of-range access, if that is enabled).
Applied consistently, the same method would ensure that the value immediately above the last representable value of an integer type is valid in ranges of that type, even if it would be invalid in an expression as an operand of that type.
Rust's solution to this is quite good, that's 0..9 and if you want to include 9 it's 0..=9, it looks a bit funny but knowing one with an = sign in it exists removes any doubt
I'm actually curious now how this is stored on `Range` in rust. I've certainly used ..= for exactly the reason you say, but as far as I'm aware `.end` on the range is the exclusive upper bound in all cases. What happens to `.end` in the overflowing case?
Edit: it doesn't use Range for ..=, but rather RangeInclusive, which works fine.