Переменные в Lua: объявление, экспорт и импорт

Переменные в Lua: объявление через local, экспорт через return и импорт через require

Опубликовано:

В Lua есть особенность: любая переменная по умолчанию создается глобальной. Забытое ключевое слово local может не только замедлить работу программы, но и привести к трудноловимым багам. В этой статье мы разберем, как правильно управлять областью видимости и как превратить обычную таблицу в полноценный изолированный модуль, используя связку return и require.

Lua — язык с динамической типизацией. Это значит, что как и в Python, тип переменных не указывается, он определяется присвоенным им значением и может меняться по ходу выполнения программы.

a = 10
b = "Hello"
print(a)
print(b)
b = 0.75
print(b)

Однако для Lua такой код не считается достаточным. Переменные, объявленные без ключевого слова local, являются глобальными для проекта. В Lua принято избегать глобальных объектов, так как они замедляют работу программы, засоряют память и приводят к скрытым ошибкам из-за случайного перезаписывания данных в разных файлах. Вместо этого для совместного использования сущностей разными файлами проекта создаются локальные таблицы, которые наполняются данными и возвращаются через return. Как это делается программно будет рассмотрено в конце.

Таким образом, чтобы код выше был корректен, имена переменных следует предварять инструкцией local, которая ограничивает область видимости переменной текущим блоком кода. В данном случае — файлом.

local a = 10
local b = 'Hello'
local c  -- по умолчанию присваивается nil
print(a)
print(b)
b = 0.75  -- изменение значения локальной переменной
print(b)
c = 1
print(c)
d = 3  -- создание глобальной переменной

Что касается функций, то переменные, объявленные выше них, являются для функций внешними локальными (замыканиями). В Lua если в теле функции происходит присвоение имени, которое совпадает с именем внешней переменной, всегда она и имеется в виду, локальная при этом не создается. Это отличается от Python.

local count = 0

local function click()
    count = count + 1
end

click()
click()

print(count)  -- 2

Для того чтобы создать локальную переменную в функции, надо, опять же, использовать инструкцию local.

local function output()
    local a = io.read()  -- если ввести: hello
    print(a..'!')  -- получится: hello!
end

output()

Если убрать local, то в Lua переменная a утечет в глобальную область видимости даже несмотря на то, что она объявлена внутри функции. Однако это не касается параметров. Lua автоматически делает их локальными.

local function output(text)
    print(text..'!')
end

output('hello')  -- hello!
output(10)  -- 10!

Если локальная переменная объявляется внутри условных операторов и циклов, то она живет до ближайшей инструкции end.

local x = 10 -- Видна во всем файле

if x > 5 then
    local x = 20 -- Создали локальную переменную внутри блока IF
    print(x)     -- Выведет: 20
end

print(x) -- Выведет: 10

Теперь вернемся к вопросу, как предоставлять доступ к локальным объектам другим файлам проекта, то есть к экспорту локальных имен. Для связи между файлами в Lua используют модули (при этом имеют в виду обычную Lua-таблицу, которая содержит внутри себя функции и переменные). Файл возвращает ссылку на таблицу в памяти через return, а другие файлы могут связать ее с локальной переменной через require.

Файл-экспортер (пример имени export.lua):

local M = {}  -- создается пустая таблица

M.a = 10 -- добавляемая в нее переменная

function M.func(b)  -- добавляемая в нее функция
    print(M.a + b)
end

return M  -- возвращаем таблицу

Файл-импортер:

local m = require("export")  -- получаем таблицу

print(m.a)  -- 10

m.func(115)  -- 125

В данном случае файлы не только должны находиться в одном каталоге, но и интерпретация второго должна запускаться из этого каталога (он должен быть текущим).

user@home:~/lua_code/variables$ lua import.lua 
10
125

Если файл будет запускаться из каталога на уровень выше, то в requare указывается путь до модуля относительного этого каталога:

local m = require("variables.export")
... 
user@home:~/lua_code$ lua variables/import.lua 
10
125

Если в первом файле не будет return, то его запрос через require из другого приведет к выполнению кода первого. В файле с таблицей-модулем могут быть свои локальные переменные и функции для внутренних нужд.

Что такое таблицы в Lua будет рассказано в следующих уроках. Здесь же заметим, что выражение подобное M.a = 10 создает не локальную переменную, а поле (ключ) внутри таблицы. Область видимости поля определяется тем, как была объявлена сама таблица. Если она локальна, то ее поля также скрыты от других файлов. При этом инструкция return открывает к ней доступ.