Today I found an unintended behavior of Lua tables.
It seems that the length of a table is not being correctly computed in the following case:
local arr = table.create(20, 1)
print("Start length: " .. #arr)
for i = 1, #arr do
if i == 10 or i == 20 then
arr[i] = nil
end
end
print("End length: " .. #arr)
This prints:
Start length: 20
End length: 9
The length of the table has changed in a way that does not make any sense.
The following code works properly though:
local arr = table.create(20, 1)
print("Start length: " .. #arr)
for i = 1, #arr do
if i == 10 then
arr[i] = nil
end
end
print("End length: " .. #arr)
and it prints
Start length: 20
End length: 20
The following also works properly:
local arr = table.create(20, 1)
print("Start length: " .. #arr)
for i = 1, #arr do
if i == 10 or i == 20 then
arr[i] = false
end
end
print("End length: " .. #arr)
and prints
Start length: 20
End length: 20
This bug has caused a few of my scripts to malfunction silently. It happens consistently in all scripts in all of my places.
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be zero. For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value. If the array has “holes” (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
After reading that excerpt from the Lua manual, it seems that the # operator has essentially undefined behavior for tables with holes. Considering that it is impossible to predict the result of #t in a table with holes, I would highly suggest that Roblox change this behavior in their new Luau implementation. This behavior is confusing, inconsistent with all other programming languages, and not useful.