Why do we need a range operator for Array index assignments?


a = [] of Int32

Compiles without a problem but it doesn’t work without a range operator:

a = [] of Int32

Unhandled exception: Index out of bounds (IndexError)

Is there a reason not to use Array#<<?

a = [] of Int32
a << 1000
a << 5
a << 12
puts a # => [1000, 5, 12]

That said, it would be nice to be able to assign via index at the end of an array. I’m not sure if that’s the way the Array class is meant to be used, though.

This is simplified version of my code, I was trying to make Array(Array(Int32)) programmatically.
And In the middle of my experiments I found this behavior, and It was a little awkward to me.

Could you use one of the Array constructors?

arr = Array(Array(Int32)).new(10) do |index|
  (index * 10...(index + 1) * 10).to_a

p! arr # => # => [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], ...]


1 Like

Thanks for your suggestion that will help, But I still do want to know the reason behind this :)))

1 Like

For index out of bounds it should raise. Can you please open a bug report?

Yes, Indeed

Can you elaborate a little more? you mean it also should raise an error with range operator?
because I thought it should work in both ways, and opened a bug report for not working like that.

Yes, it should raise on both cases when the start of the range is out of bounds. I think if you read from the array with a range it will raise, so we probably missed to add that check when writing.


a = [] of Int32
pp a[0..]

return []

a = [] of Int32
pp a[0]

raise an error

The reason is that those operators mean write or replace, never add. Ruby is the only language that doesn’t do that, and that’s bug prone. What if you wanted to replace an item and ended up adding one? How can you find our you have a bug? You can’t, at least easily.

1 Like

Try with 1…

0… works because it’s right at the end of the array, which is empty, so there’s nothing to replace. If you try it in Ruby, it returns nil on index out of bounds, otherwise it returns an Array. Crystal is the same except that we raise instead of returning nil. We allow the start index to match the size of the array, like in Ruby.

by this explanation

a = [] of Int32

should also works which it doesn’t.

That is reasonable, I’ve changed GitHub issue title and text.

The idea of range is that if you have this:

a = [1, 2, 3]

If you say a[3..10] means “take everything that begins at index 3 and continues until index 10”. Since the array ends at index 3, there’s nothing else to take and [] is returned. However, if you say a[4..10] then the index 4 is already out of bounds.

When you say a[0] = 1 for an empty array, 0 is already out of bounds because it will cause a change in the array size, and we don’t allow that.

These are just design decisions. We could change it to make it work like in Ruby, but we prefer not to.