I'm proposing to let array[array.size] = x
behave exactly the same as array << x
instead of raising IndexError
.
Currently, when calling Array#[]=(index : Int, value : T)
valid values for index
are 0...size
(exclusive range). When called with index >= size
it raises IndexError
:
ary = [1, 2, 3]
ary[2] = 1 # OK
ary[3] = 1 # IndexError
ary[4] = 1 # IndexError
This behaviour is generally correct. The array can't be dynamically resized to index
because array[size...index]
would be void. There is no default value for array elements.
For the edge case index == size
, however this is not a problem: It would not create a gap, simply append an element to the end.
ary = [1, 2, 3]
ary[2] = 1 # OK
ary[3] = 1 # OK
ary[4] = 1 # IndexError
I encountered a use case for this when using an array as a stack with a stack_pointer referencing the array index. When the pointer moves to the right, it needs to make sure the array grows.
# this is how I tried to do it:
stack_pointer += 1
stack[stack_pointer] = nil
# this is how it ended up:
stack_pointer += 1
if stack.size <= stack_pointer
stack.push nil
end
With this change, the first alternative would work as expected.
I'm not sure.
[]=
mutates an array in place with no side effect: the array won't be resized or reallocated, unlike growing (or possibly shrinking) mutators like push
where the resize is expected. With that change, there would be one special case where []=
would suddenly grow the array. This is IMO inconsistent and unexpected, and could silence an actual error.
BTW: your example would never raise an IndexError but grow the array, unless you create a gap in indexes, for example jumping from index 3 to 5.
I think there can be many "off by one" errors that with this change will start to work, incorrectly.
Also, for stack usage you can use push and pop.
What about making #insert
have this behavior?
Wouldn't #[]=
working in some cases but not others be more confusing?
Explicitly named method #insert
mentioned by @Blacksmoke16 can be less confusing for this case, but I'm not sure.
insert
already changes the length of the array, and appending elements would be consistent with the existing behavior.
Closing. We won't implement this.
Inserting at the end of an array could be semantically correct. It's basically #push
but with the ability to insert at any place within 0..size
(inclusive) instead of 0...size
(exclusive) when we can already insert at zero (basically #unshift
), but it wouldn't solve the initial issue, which isn't about inserting, but replacing and sometimes have an insertion that can already be achieved with #push
.
Most helpful comment
I think there can be many "off by one" errors that with this change will start to work, incorrectly.
Also, for stack usage you can use push and pop.