Constant box.NULL
There are some major problems with using Lua nil values in tables. For example: you can’t correctly assess the length of a table that is not a sequence. (Learn more about data types in Lua and LuaJIT.)
Example:
tarantool> t = {0, nil, 1, 2, nil}
---
...
tarantool> t
---
- - 0
- null
- 1
- 2
...
tarantool> #t
---
- 4
...
The console output of t
processes nil values in the middle and at
the end of the table differently. This is due to undefined behavior.
Note
Trying to find the length for sparse arrays in LuaJIT leads to another scenario of undefined behavior.
To avoid this problem, use Tarantool’s box.NULL
constant instead of nil.
box.NULL
is a placeholder for a nil value in tables to preserve a key
without a value.
box.NULL
is a value of the cdata type representing a NULL pointer.
It is similar to msgpack.NULL
, json.NULL
and yaml.NULL
. So it is
some not nil value, even if it is a pointer to NULL.
Use box.NULL
only with capitalized NULL (box.null
is incorrect).
Note
Technically speaking, box.NULL
equals to ffi.cast('void *', 0)
.
Example:
tarantool> t = {0, box.NULL, 1, 2, box.NULL}
---
...
tarantool> t
---
- - 0
- null # cdata
- 1
- 2
- null # cdata
...
tarantool> #t
---
- 5
...
Note
Notice that t[2]
shows the same null
output in both examples.
However in this example t[2]
and t[5]
are of the cdata type, while
in the previous example their type was nil.
Important
Avoid using implicit comparisons with nullable values when using box.NULL
.
Due to Lua behavior,
returning anything except false or nil from a condition expression
is considered as true. And, as it was mentioned earlier, box.NULL
is a
pointer by design.
That is why the expression box.NULL
will always be considered true in case
it is used as a condition in a comparison. This means that the code
if box.NULL then func() end
will always execute the function func()
(because the condition box.NULL
will
always be neither false nor nil).
Use the expression if x == nil
to check if the x
is either a nil
or a box.NULL
.
To check whether x
is a nil but not a box.NULL
, use the following
condition expression:
type(x) == 'nil'
If it’s true, then x
is a nil, but not a box.NULL
.
You can use the following for box.NULL
:
x == nil and type(x) == 'cdata'
If the expression above is true, then x
is a box.NULL
.
Note
By converting data to different formats (JSON, YAML, msgpack), you shall expect
that it is possible that nil in sparse arrays will be converted to
box.NULL
. And it is worth mentioning that such conversion might be
unexpected (for example: by sending data via net.box
or by obtaining data from spaces etc.).
tarantool> type(({1, nil, 2})[2])
---
- nil
...
tarantool> type(json.decode(json.encode({1, nil, 2}))[2])
---
- cdata
...
You must anticipate such behavior and use a proper condition expression.
Use the explicit comparison x == nil
for checking for NULL in nullable values.
It will detect both nil and box.NULL
.