Reproduction Steps
- In the output window or in a script, print out
#array
with the third index missing:
print(#{[1] = 1, [2] = 2, [4] = 4, [5] = 5})
- Run the code
- Observe that it doesn’t print out
2
Note that this only works on arrays with a missing third index and ipairs
works as expected when looping through this array
Expected Behavior
- In the output window or in a script, print out
#array
with the third index missing:
print(#{[1] = 1, [2] = 2, [4] = 4, [5] = 5})
- Run the code
- Prints out
2
Actual Behavior
Doesn’t print out 2, but the highest consecutive index after the third index (in this case, 5
).
Issue Area: Engine
Issue Type: Other
Impact: Low
Frequency: Constantly
#
only behaves the way you expect on arrays, which are sequences of non-nil values starting at index 1 up to an index N, leaving no holes.
Since you have a hole at index 3, your sequence stops at index 2, so only up to that value is considered.
This is covered in the Lua 5.1 spec that Luau is based off: Lua 5.1 Reference Manual
Luau does not spec out #
specifically (see Syntax - Luau) so it inherits the Lua 5.1 spec for that operator as in the link above.
This is not a bug, it conforms to the spec.
2 Likes
To add to @buildthomas’s answer above, the #
operator will do a binary search for the end of the array.
An array is defined as a table with numeric keys 1-n and no further keys after n. However, your array contains holes, meaning you get undefined behavior that should not happen per spec, and thus can be optimized with the assumption that you will never allow an array with holes in it to be #
'd.
Compare these two lines:
print(#{[1] = 1, [2] = 2, [4] = 4, [5] = 5}) -- prints 5
print(#{[1] = 1, [2] = 2, [3] = 3, [5] = 5}) -- prints 3
In the first case, it checks for 1, then 2, then 4, then 8, then 6, then 5.
In the second case, it checks for 1, then 2, then 4, then 3.
ipairs
works as expected because it doesn’t need to find the end of the array in advance, it needs to iterate through the array one step at a time anyway.
2 Likes