[]

Hello

The problem is caused by negative indices:

a=[1,2,3]

a[-3..-1] does work # ==a

a[-4..-1] # -> nil # should be [1,2,3] # -> this could be
considered as Ruby-Bug

Does someone want to make a bugreport?

Moreover I think [nil,1,2,3] is more correct than [1,2,3]. - Which one
is better?

Opti

I don’t recall exactly where I first heard this, but think of the index as pointing "between" the elements and it might make more sense:

For an array of "N" elements:

       0 1 2 3 … N-3 N-2 N-1 N >N
       > > > > > > > > >
      [ 1, 2, 3, …, N-2, N-1, N ]
   > > > > > > > >
< -N -N -(N-1) -(N-2) -(N-3) -3 -2 -1

Only those positions that fall "within the brackets" are valid index values.

In any case, it is *not* a bug. This is how the behavior is defined.

-Rob

···

On 11 Aug 2022, at 10:55, Die Optimisten <inform@die-optimisten.net> wrote:

Hello

The problem is caused by negative indices:

a=[1,2,3]

a[-3..-1] does work # ==a

a[-4..-1] # -> nil # should be [1,2,3] # -> this could be
considered as Ruby-Bug

Does someone want to make a bugreport?

Moreover I think [nil,1,2,3] is more correct than [1,2,3]. - Which one
is better?

Opti

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

Hello

The problem is caused by negative indices:

a=[1,2,3]

a[-3..-1] does work # ==a

a[-4..-1] # -> nil # should be [1,2,3] # -> this could be
considered as Ruby-Bug

Does someone want to make a bugreport?

I'm not sure if this is a bug. As documented at

"When a single Range argument range is given, treats range.min as start
above and range.size as length above"

Given your example:

(-4..-1).min    # -> -4
(-4..-1).size   # -> 4

# Same as a[-4..-1]
a[-4, 4]      # -> nil

I can't find a discussion in the documentation what should happen in the
specific case where the start argument is outside the range of the array;
however, there is a discussion about the case where the index argument for
the single argument form of the method call is out of range:

"If index is negative, counts relative to the end of self... If index is
out of range, returns nil."

It seems that the implementation of #[start, length] short circuits in your
case because start is out of range for the array. The length argument is
ignored and nil is returned. If there is a bug here, I think it's a
documentation bug that could be corrected by discussing this particular
case.

Moreover I think [nil,1,2,3] is more correct than [1,2,3]. - Which one

is better?

Personally, I think raising RangeError is better. When I'm slicing arrays,
using either a Range or start and length arguments, I expect to get an
array out. I don't expect to get nil. I don't want to do like golang and
litter my code with conditionals looking for nil in case I mess up indexing
into the array. I would prefer to get an exception right at the point the
mistake is made rather than get a mysterious "NoMethodError for
nil:NilClass" somewhere down the line. I think this should also happen for
the "index out of range case" too for much the same reason, namely that I
probably don't expect to get a nil out of my array and would prefer to know
I actually attempted to index outside the range of the array instead of
tracking down where an unexpected nil came from later in the code.

In any case, I think returning either of the array outputs you suggest is
similarly confusing and a potential source of hard to diagnose bugs in
downstream code. Ruby would help me much more as a developer by telling me
that I'm using an array in a nonsensical way than trying to hide that from
me by giving surprising values, even if documentation fully covers those
cases.

-Jeremy

···

On Thu, Aug 11, 2022 at 9:55 AM Die Optimisten <inform@die-optimisten.net> wrote:

I 100% disagree, the fact that I can do `if array[index]` is one of
the things I like most about Ruby. I don't want to be dealing with
exceptions and I can't believe even C is more friendly than many
languages in this respect.

Given that, if a[-4] is nil, then I would expect a[-4..x][0] to be nil.

I would expect these to be the same:

    a[-4..3] == (-4..3).map { |e| a[e] } == [a[-4], a[-3], a[-2],
a[-1], a[0], a[1], a[2], a[3]]

And I don't see why anyone else wouldn't.

···

On Thu, Aug 11, 2022 at 10:44 AM Jeremy Bopp <jeremy@bopp.net> wrote:

On Thu, Aug 11, 2022 at 9:55 AM Die Optimisten <inform@die-optimisten.net> wrote:

Moreover I think [nil,1,2,3] is more correct than [1,2,3]. - Which one
is better?

Personally, I think raising RangeError is better. When I'm slicing arrays, using either a Range or start and length arguments, I expect to get an array out. I don't expect to get nil. I don't want to do like golang and litter my code with conditionals looking for nil in case I mess up indexing into the array. I would prefer to get an exception right at the point the mistake is made rather than get a mysterious "NoMethodError for nil:NilClass" somewhere down the line. I think this should also happen for the "index out of range case" too for much the same reason, namely that I probably don't expect to get a nil out of my array and would prefer to know I actually attempted to index outside the range of the array instead of tracking down where an unexpected nil came from later in the code.

--
Felipe Contreras

Hi

a=[1,2,3,4,5]

It seems to be documented as it is ( a[-10,10] => nil )

But I don't understand why positive indices (at the end) are allowed (
a[1,10] )
whereas this is not the case at the left side (with negative indices:
a[-10,10] ) # also a[-3,10] works !
I would expect a more "consistent" behaviour. - So I see the current
behaviour as bug (in the language).

It could be useful to have a[outbound_range] returning the not-available
elements as nil, we can use .compact for removing this feature. ( +
.lcompact. and .rcompact for left/right side only)

a should never fail, whereas a.fetch(i) can report errors
-> there should be also a.fetch(range) !

Opti

> Personally, I think raising RangeError is better. When I'm slicing
arrays, using either a Range or start and length arguments, I expect to get
an array out. I don't expect to get nil. I don't want to do like golang
and litter my code with conditionals looking for nil in case I mess up
indexing into the array. I would prefer to get an exception right at the
point the mistake is made rather than get a mysterious "NoMethodError for
nil:NilClass" somewhere down the line. I think this should also happen for
the "index out of range case" too for much the same reason, namely that I
probably don't expect to get a nil out of my array and would prefer to know
I actually attempted to index outside the range of the array instead of
tracking down where an unexpected nil came from later in the code.

I 100% disagree, the fact that I can do `if array[index]` is one of
the things I like most about Ruby. I don't want to be dealing with
exceptions and I can't believe even C is more friendly than many
languages in this respect.

While that does make some things easier, it's terrible behavior when
debugging a large and unfamiliar code base. Getting nils in unexpected
places usually leaves you with a lot of backtracking to do from a
NoMethodError stack trace. This may be fine for you if your array may
genuinely contain nils, but if it doesn't then you need to take care that
you don't inadvertently propagate a nil due to an out of range reference
into the array. Doing that requires you to add a conditional somewhere to
either ensure that the array index is in range or that you do something
special if nil is returned.

Bounds checking is important in any programming language, even Ruby. If a
RangeError was raised in such cases, you wouldn't have to add any special
conditionals if you didn't intend to allow out of bounds references. Your
program just crashes with an exception, one that is very explicit about
what happened and why, Compare that to the NoMethodError case which can
happen anywhere downstream of where the lookup happened and completely out
of context. What's also nice is that you could safely embed nils into your
array and know the difference between a valid index to a nil value vs. an
invalid index. You have an opportunity to handle the invalid index case in
a special way without performing an explicit bounds check yourself if so
desired.

Given that, if a[-4] is nil, then I would expect a[-4..x][0] to be nil.

Yeah, I can see your reasoning. I just feel it hides a corner case in a
way that is hard to deal with if you ever need or want to. I've been
coding for almost 30 years, and I can't recall a time that I deliberately
wanted to index out of bounds in an array, whether reading from or writing
to it. I'm not saying there aren't use cases for Ruby's current behavior,
but out of bounds indexing is probably most often unintentional and the
result of a bug. It would be good if Ruby helped to catch it sooner and in
a more obvious way, IMO.

-Jeremy

···

On Thu, Aug 11, 2022 at 11:05 AM Felipe Contreras < felipe.contreras@gmail.com> wrote:

On Thu, Aug 11, 2022 at 10:44 AM Jeremy Bopp <jeremy@bopp.net> wrote:

Hi

a=[1,2,3,4,5]

It seems to be documented as it is ( a[-10,10] => nil )

But I don't understand why positive indices (at the end) are allowed (
a[1,10] )
whereas this is not the case at the left side (with negative indices:
a[-10,10] ) # also a[-3,10] works !
I would expect a more "consistent" behaviour. - So I see the current
behaviour as bug (in the language).

Just to be clear, the second number is a length and not an index in this
case. As a length, you could argue that the behavior means, "collect at
most length elements". Does that make it seem more consistent? Maybe. Is
it still potentially dangerous for the programmer? Possibly.

It could be useful to have a[outbound_range] returning the not-available

elements as nil, we can use .compact for removing this feature. ( +
.lcompact. and .rcompact for left/right side only)

These steps are inefficient due to the creation of intermediate arrays.
It's also extra code to remember to use. Obviously, it risks breaking
existing code.

a should never fail, whereas a.fetch(i) can report errors
-> there should be also a.fetch(range) !

This is actually a pretty nice idea. A method that ensures strict bounds
checking but otherwise behaves like # and #slice would be quite helpful.
If you open a feature request for this, it might be accepted.

···

On Thu, Aug 11, 2022 at 11:14 AM Die Optimisten <inform@die-optimisten.net> wrote:

> Personally, I think raising RangeError is better. When I'm slicing arrays, using either a Range or start and length arguments, I expect to get an array out. I don't expect to get nil. I don't want to do like golang and litter my code with conditionals looking for nil in case I mess up indexing into the array. I would prefer to get an exception right at the point the mistake is made rather than get a mysterious "NoMethodError for nil:NilClass" somewhere down the line. I think this should also happen for the "index out of range case" too for much the same reason, namely that I probably don't expect to get a nil out of my array and would prefer to know I actually attempted to index outside the range of the array instead of tracking down where an unexpected nil came from later in the code.

I 100% disagree, the fact that I can do `if array[index]` is one of
the things I like most about Ruby. I don't want to be dealing with
exceptions and I can't believe even C is more friendly than many
languages in this respect.

While that does make some things easier, it's terrible behavior when debugging a large and unfamiliar code base. Getting nils in unexpected places usually leaves you with a lot of backtracking to do from a NoMethodError stack trace. This may be fine for you if your array may genuinely contain nils, but if it doesn't then you need to take care that you don't inadvertently propagate a nil due to an out of range reference into the array. Doing that requires you to add a conditional somewhere to either ensure that the array index is in range or that you do something special if nil is returned.

I'm a big boy, if I can deal with allocating/freeing memory in C and
handling multiple threads with locks and conditions and mapping and
unmapping hardware memory with DMA, surely I can deal with arrays of
objects in Ruby.

Given that, if a[-4] is nil, then I would expect a[-4..x][0] to be nil.

Yeah, I can see your reasoning. I just feel it hides a corner case in a way that is hard to deal with if you ever need or want to. I've been coding for almost 30 years, and I can't recall a time that I deliberately wanted to index out of bounds in an array, whether reading from or writing to it.

I've been coding for almost 30 years as well, and I view arrays the
complete opposite way: the size of an array is an irrelevant detail at
the moment of reading or writing to a certain index. I understand that
if I create an array of size 10, and I want to write to index 10, I
need to increase the size of the array, in C, because that's how
memory works in C, but not in Ruby. I should never need to increase
the size of an array in Ruby.

I'm not saying there aren't use cases for Ruby's current behavior, but out of bounds indexing is probably most often unintentional and the result of a bug. It would be good if Ruby helped to catch it sooner and in a more obvious way, IMO.

And I disagree, the current behavior is fine.

And given that a[-4] returns nil *today*, a[-4..x][0] not returning
nil is a bug.

···

On Thu, Aug 11, 2022 at 9:27 PM Jeremy Bopp <jeremy@bopp.net> wrote:

On Thu, Aug 11, 2022 at 11:05 AM Felipe Contreras <felipe.contreras@gmail.com> wrote:

On Thu, Aug 11, 2022 at 10:44 AM Jeremy Bopp <jeremy@bopp.net> wrote:

--
Felipe Contreras