# Operator Incorrectly Identifies Arrays Without A Third Index

Reproduction Steps

  1. 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})
  1. Run the code
  2. 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

  1. 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})
  1. Run the code
  2. 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