lua pitfalls
quirks can (and will) come around when migrate from one language to another, as languages often have different interpretations and implementations; it would be helpful if prominent inconsistencies are logged to avoid going over the getting started chapters again and again; this article is a list of pitfalls for people migrating to lua (from an unknown whatever language); in spite of word “pitfall” in title, this article does not mean to be a verdict about better or worse, but just poses the differences as they are;
here the lua reference in use is Programming in Lua (first edition) that aims at lua 5.0; these days most people would be on lua 5.4, but we are getting started, right? for those who want the latest language reference visit the page Lua 5.4 Reference Manual;
code examples below are in lua 5.4; so the code may not entirely conform to the lua reference in use;
each item has a series of ref tags that can be followed to find authentic information:
-
(ref) a chunk can be a single line in interactive mode;
-
(ref) global variables do not need to be declared or deleted; they can be assigned, and they are existent iff non-nil;
-
(ref) lua is case-sensitive;
-
(ref) lua has 8 basic types: nil, boolean, number, string, userdata, function, thread, and table;
-
(ref)
nil
is different from anything butnil
:> print(nil == nil) true > print(nil == false) false > print(nil == 0) false > print(nil == '') false > print(nil == {}) false
-
(ref)
false
andnil
are false, everything else is true; zero0
is true; empty string''
is true; -
(ref) lua numbers are all (double) floats; lua has no integer type, because lua floats have no rounding error upto
100,000,000,000,000
; -
(ref) strings are immutable;
-
(ref) single-quoted and double-quoted literal strings are the same;
-
(ref) lua has automatic conversions between numbers and strings:
> print(type("12" + 34), "12" + 34) number 46 > print(type("12" .. 34), "12" .. 34) string 1234
-
(ref) use
tonumber
andtostring
for manual conversions between numbers and strings; -
(ref) lua table is associative array (aka: dict, map);
-
(ref) all types except
nil
can be used as table key (aka: index); and key types can be mixed; -
(ref) table values evaluate to
nil
if not initialized; -
(ref) use table with integer keys to implement conventional array;
-
(ref) use
ipairs
to iterate conventional array; -
(ref) lua supports functional programming; functions are first-class citizens;
-
(ref) relational operators always result in
true
orfalse
; -
(ref) lua inequality operator is
~=
(not!=
); -
(ref) values of different types are not equal:
> print(0 == '0') false > print(0 == '') false > print(0 == {}) false > print(0 == false) false > print(0 == nil) false
-
(ref) lua compares tables, userdata, and functions by reference:
> a = {} > b = {} > c = a > a == b false > a == c true
-
(ref) logical operators are
and
,or
,not
; -
(ref) logical operators consider
false
andnil
as false and anything else as true; -
(ref) both
and
andor
use shortcut evaluation; -
(ref) operator
not
always returnstrue
orfalse
:> print(not nil) true > print(not false) true > print(not 0) false > print(not '') false > print(not {}) false
-
(ref) one can use
(not not ...)
to convert any value to boolean; -
(ref) operator
not
has higher precedence than relational operators (eg:==
):> print(not not 0 == 0) false > print((not not 0) == 0) false > print(not not (0 == 0)) true
-
(ref) table constructor can mix explicit and implicit indexes:
> x = {'a', 'b', [8]='c', 'd', 'e'} > x[1] a > x[2] b > x[3] d > x[4] e > x[8] c
-
(ref) mixing explicit and implicit indexes in table constructor can cause trouble:
> x = {'a', 'b', [2]='c', 'd', 'e'} > x[1] a > x[2] b > x[3] d > x[4] e > x[2] b
-
(ref) in multiple assignment, values are evaluated then assigned; so swap works:
> x, y = 3, 8 > x, y = y, x > print(x, y) 8 3 > a = {4, 9} > a[1], a[2] = a[2], a[1] > print(a[1], a[2]) 9 4
-
(ref) in multiple assignment, values are adjusted to variables; extra variables are assigned
nil
; extra values are discarded; -
(ref) local variables are created with keyword
local
; -
(ref) local variables have block scope; the scope begins after the declaration and goes to end of block;
local x = 1 for i = 1, 2 do print(x) local x = i * 2 print(x) end
1 2 1 4
-
(ref) local variable declaration with assignment has same rule: extra variables are assigned
nil
; extra values are discarded; -
(ref) local variable declaration without assignment initializes all variables with
nil
; -
(ref) one can create an explicit block with
do-end
: -
(ref) condition expression in
if
,while
, etc. may be any value:false
andnil
are treated as false, others true; -
(ref) numeric
for
: all three expressions are evaluated only once; -
(ref) numeric
for
: loop variables are local variables visible only inside the loop; -
(ref) numeric
for
: never change the value of loop variables; -
(ref) generic
for
: loop variables are local variables visible only inside the loop; -
(ref) generic
for
: never change the value of loop variables; -
(ref) generic
for
: useipairs
to traverse conventional arrays; usepairs
to traverse associative arrays; -
(ref) there is an implicit
return
at the end of any function that returns nothing; -
(ref) one can use colon
:
for object-oriented calls (aka: methods):o:foo(x)
is the same aso.foo(o, x)
; -
(ref) function parameters work like local variables, initialized with function arguments;
-
(ref) extra function parameters are assigned
nil
; extra function arguments are discarded; -
(ref) lua lacks default arguments;
-
(ref) functions can return multiple results; the number of results depend on the circumstances of the call;
-
(ref) a function call used as an expression gives only the first result;
-
(ref) a function call gives all results only when it is the last expression in a list of expressions;
-
(ref) function results are appended with
nil
as needed; -
(ref) table constructor collects all results from a call, without any adjustments;
-
(ref) one can force a call to return exactly one result using an extra pair of parentheses:
-
(ref)
return f()
returns all values returned byf
; -
(ref)
return (f())
returns one single value; -
(ref)
unpack
takes an array and returns all its elements as results (starting from 1); -
(ref) three dots
...
parameter collects a variable number of arguments into hidden tablearg
; -
(ref) a function can have some fixed parameters plus a variable number of parameters;
-
(ref) lua lacks named arguments; simulate that by passing a table argument;
-
(ref) functions are anonymous:
function f (x) return x+1 end --same f = function (x) return x+1 end --same
-
(ref)
local function
syntactic sugar handles recursive functions, but not indirect recursive functions;