if you are thinking in terms of 'c' - which i tend to - an offset of zero
from
a thing is the same as the thing. but that clearly is not what's going on
here. i think you are right to call that a bug.
I believe that you are intentionally allowed to go 'one past the end' when
taking slices, for convenience/symmetry reasons.
irb(main):001:0> a=["x","y"]
=> ["x", "y"]
irb(main):002:0> a[0]
=> "x"
irb(main):003:0> a[1]
=> "y"
irb(main):004:0> a[2]
=> nil
irb(main):005:0> a[0,1]
=> ["x"]
irb(main):006:0> a[1,1]
=> ["y"]
irb(main):007:0> a[2,1] #<<<< note
=> [] #<<<<
irb(main):008:0> a[3,1]
=> nil
irb(main):009:0>
It makes sense if you think of:
irb(main):009:0> a[0..-1]
=> ["x", "y"]
irb(main):010:0> a[1..-1]
=> ["y"]
irb(main):011:0> a[2..-1]
=> []
as symmetrical to
irb(main):012:0> a[0,2]
=> ["x", "y"]
irb(main):013:0> a[0,1]
=> ["x"]
irb(main):014:0> a[0,0]
=> []
That is, a legitimate array slice operation should return an array, and if
the original array is of size N, we can expect a slice of size between 0 and
N. In that case there are N+1 "sensible" starting positions. Any starting
position outside 0..N is considered invalid and returns nil. (Whether that
should actually raise an exception is off-topic
In the case of a zero-sized array (N=0): then index 0 is "one past the end".
irb(main):015:0> [][0,1]
=> []
irb(main):016:0> [][1,1]
=> nil
A bit woolly, but perhaps someone else can put it better.
Regards,
Brian.