Lua is correct.
Your first example is forming a table that contains exactly three entries with indices 1, 2, and 3, none of which are specified explicitly. In this case table.maxn()
, the #
operator, and lua_objlen()
all agree that there are three array elements.
Your second example is forming a table that contains exactly three entries with indices "Talk", "Talk2", and "Talk3", all specified explicitly but none are integers. In this case, table.maxn()
, the #
operator, and lua_objlen()
all agree that there are zero array elements.
Why is this the right answer?
A Lua table
is an associative array that can map values of any type (except nil
) to values of any type (again, except nil
). There is no other general container type in Lua, so tables are used for essentially everything. The implementation is not part of the Lua specification, but in practice a table is indexed as a hash table, and there is no natural ordering of the keys.
However, a common use case for tables is as a container that acts like a conventional array. Such a container has contiguous integer indices, and can be iterated in order. The table implementation makes this particularly efficient for integer keys starting at 1
. These entries in the table are physically allocated as a contiguous array, and the keys are neither hashed nor stored. This saves storage space both in allocation overhead and by not storing some of the keys at all. It also saves run time since access is by direct calculation rather than by computing a hash and verifying the matching value.
Since arrays are just tables, the table initializer syntax has been designed to make that case easy and clear, as well as to support the other common use case where keys are strings that happen to be valid identifiers.
Lua 5.1 vs. Lua 5.0
In the current release of Lua (5.1.4, but this is true of all 5.1 releases) the Lua 5.0 functions table.getn()
, table.setn()
, luaL_getn()
, and luaL_setn()
are all deprecated, as is the common usage of a table field n
for an array to indicate its array length. The table.getn()
function is replaced by the #
operator, and luaL_getn()
by lua_objlen()
. There is no equivalent to the setn()
functions since Lua now manages the array size behind the scenes.
The #
operator is defined on a table to return an integer such that indexing the table by the next larger integer returns nil
. For an empty array (e.g. a = {}
), it returns 0 because a[1] == nil
. For a normal array, it returns the largest index in use. However, if an array has holes (missing elements) then it can return the index preceding any hole or the last index in use. If the actual largest integer index in use is required, the new function table.maxn()
can be used, but it has to iterate over all table entries to find that maximum value and thus should only be used when using #
can't.
This leads to a now common idiom for adding elements to an array: a[#a+1] = "some new value"
. This idiom is now often recommended in place of table.insert()
for extending tables.