More Lua Gotchas
Speaking of Lua gotchas, here’s another one. In Lua, a function with n parameters is similar to a variadic function. As an example, consider:
function f(a, b)
print(a, b)
end
We can call f
with as many arguments as we like. Missing arguments become
nil
, extra arguments are discarded:
f() -- nil nil
f(1) -- 1 nil
f(1, 2) -- 1 2
f(1, 2, 3) -- 1 2
In fact, function f
could be written as:
function f(...)
local a, b = ...
print(a, b)
end
The reference manual has the following to say about local a, b = ...
:
Before the assignment, the list of values is adjusted to the length of the list of variables. If there are more values than needed, the excess values are thrown away. If there are fewer values than needed, the list is extended with as many nil’s as needed. If the list of expressions ends with a function call, then all values returned by that call enter the list of values, before the adjustment (except when the call is enclosed in parentheses; see §3.4).
There is no observable difference, whether we call f(1)
or f(1, nil)
. But
for some functions, there is. Suppose we have
table.insert(t, g())
where t
is a table and g
is a function that may or may not return a value.
As long as g
returns a value, including nil
, everything works as expected.
(We don’t mind appending nil
to a table.) When g
happens to return
nothing, table.insert
complains about a missing argument, which may be
surprising, considering that Lua is supposed to adjust the number of
arguments to the number of parameters:
stdin:1: wrong number of arguments to 'insert'
stack traceback:
[C]: in function 'insert'
stdin:1: in main chunk
[C]: in ?
We can introduce a variable
local b = g()
table.insert(t, b)
or put parentheses around the call to g
table.insert(t, (g()))
to force exactly one result, turning nothing into nil
, as if g
returned
nil
. And nil
is not nothing. All three functions
function f() end
function g() return end
function h() return nil end
are interchangeable when used liked this:
local x = f(); print(x) -- nil
local y = g(); print(y) -- nil
local z = h(); print(z) -- nil
But only h
returns nil
, whereas f
and g
return nothing:
print(f()) --
print(g()) --
print(h()) -- nil