Константа box.NULL
Имеется целый ряд серьезных проблем при использовании значения nil из Lua в таблицах. Например: вы не можете корректно оценить длину таблицы, не являющейся последовательностью. (Узнайте больше о типах данных в Lua и LuaJIT.)
Пример:
tarantool> t = {0, nil, 1, 2, nil}
---
...
tarantool> t
---
- - 0
- null
- 1
- 2
...
tarantool> #t
---
- 4
...
Вывод в консоль t
обрабатывает значения nil в середине и в конце таблицы по-разному. Это вызвано неопределённым поведением.
Примечание
Попытка найти длину для разреженного массива в LuaJIT приводит к другому случаю неопределённого поведения.
Для избежания этой проблемы используйте имеющуюся в Tarantool константу box.NULL
вместо значения nil. box.NULL
является местозаполнителем для значения nil в таблицах с целью сохранения ключа без значения.
box.NULL
является значением типа cdata, представляющим нулевой указатель (NULL pointer). Оно подобно msgpack.NULL
, json.NULL
и yaml.NULL
. Таким образом, оно является некоторым не nil значением, даже если является указателем на NULL.
Используйте box.NULL
только с NULL, написанным заглавными буквами (box.null
является ошибкой).
Примечание
Технически, box.NULL
соответствует ffi.cast('void *', 0)
.
Пример:
tarantool> t = {0, box.NULL, 1, 2, box.NULL}
---
...
tarantool> t
---
- - 0
- null # cdata
- 1
- 2
- null # cdata
...
tarantool> #t
---
- 5
...
Примечание
Заметьте, что t[2]
демонстрирует один и тот же вывод null
в обоих примерах. Однако, в данном примере t[2]
и t[5]
являются типом cdata, в то время как в предыдущем примере их тип был nil.
Важно
Избегайте использования неявных сравнений с обнуляемыми (nullable) значениями при использовании box.NULL
. В связи со штатным поведением Lua, возвращение любого результата, кроме false (ложь) или nil (ничто), из выражения условия считается возвращением true (истина). Как и упоминалось ранее, box.NULL
является указателем.
Поэтому выражение box.NULL
всегда будет расцениваться как true (истина) в случае использования в качестве условия в сравнении. Это означает, что код
if box.NULL then func() end
всегда будет выполнять функцию func()
(потому, что условие box.NULL
всегда будет не false (ложь) и не nil (ничто)).
Используйте выражение x == nil
для проверки того, является ли x
nil или box.NULL
.
Для выяснения того, является ли x
в действительности nil, но не box.NULL
, используйте следующее условие:
type(x) == 'nil'
Если оно истинно (true), то x
– это nil, но не``box.NULL``.
Вы можете использовать следующее выражение для box.NULL
:
x == nil and type(x) == 'cdata'
Если вышеуказанное выражение истинно (true), то x
– это box.NULL
.
Примечание
Конвертируя данные в различные форматы (JSON, YAML, msgpack), вы должны ожидать возможного преобразования всех nil в разреженных массивах в box.NULL
. Стоит ответить, что конвертация может происходить неожиданно (например: при отправке данных через net.box или при получении данных из спейсов и т.п.).
tarantool> type(({1, nil, 2})[2])
---
- nil
...
tarantool> type(json.decode(json.encode({1, nil, 2}))[2])
---
- cdata
...
Вы должны ожидать подобное поведение и использовать соответствующее выражение условия. Используйте явное сравнение x == nil
для проверки на отсутствующее значение (NULL) в обнуляемых (nullable) переменных. Оно позволит обнаружить как nil, так и box.NULL
.