Модули и require

Учимся разбивать программу на переиспользуемые файлы-модули.

Модуль — это отдельный файл Lua, который возвращает таблицу с функциями, чтобы другие файлы могли её подключить и использовать.

Когда программа растёт, держать весь код в одном файле невозможно. Модули позволяют разложить логику по файлам: математика в одном, графика в другом.

Создание модуля

Модуль — это файл, который собирает свои функции в таблицу и возвращает её через return. Пусть это файл mathutils.lua:

-- файл mathutils.lua
local M = {}

function M.double(x)
  return x * 2
end

function M.half(x)
  return x / 2
end

return M

Главное — последняя строка return M: она отдаёт таблицу наружу.

Подключение через require

В другом файле подключаем модуль функцией require (без расширения .lua):

-- файл main.lua
local mathutils = require("mathutils")

print(mathutils.double(21))
print(mathutils.half(10))

Вывод:

42
5

require ищет файл, исполняет его и возвращает то, что тот вернул — нашу таблицу M.

Кэширование модулей

Важная особенность: модуль исполняется только один раз. Повторный require того же модуля вернёт уже готовый результат из кэша:

local a = require("mathutils")
local b = require("mathutils")
-- a и b — это одна и та же таблица

Это удобно: тяжёлая настройка внутри модуля выполнится один раз, сколько бы файлов его ни подключали.

Схема модульной структуры

main.lua
  ├─ require("mathutils")  ──> mathutils.lua  (return M)
  └─ require("graphics")   ──> graphics.lua   (return G)

Как работает под капотом

Lua хранит загруженные модули в таблице package.loaded. При вызове require("mathutils") сначала проверяется, нет ли модуля там; если есть — возвращается готовый. Если нет — файл ищется по путям из package.path, исполняется, а результат кладётся в кэш.

Частые ошибки

  • Писать require("mathutils.lua") с расширением — нужно без него.
  • Забыть return M в конце модуля — тогда require вернёт true вместо таблицы.
  • Ждать, что модуль перечитается при втором require — он берётся из кэша.

Итог

  • Модуль — файл, собирающий функции в таблицу и возвращающий её через return.
  • require("имя") подключает модуль без расширения и возвращает его таблицу.
  • Модули кэшируются: исполняются один раз, повторный require отдаёт готовое.
  • Загруженные модули хранятся в package.loaded.
Проверьте себя
1. Что должен вернуть файл-модуль, чтобы его функции были доступны через require?
AНичего, всё автоматически глобально
BТаблицу с функциями через return
CСтроку с именем модуля
DЧисло 0 при успехе
2. Что вернёт второй вызов require того же модуля?
AЗаново исполнит файл
BВернёт закэшированный результат, файл не исполняется повторно
CВернёт nil
DВызовет ошибку дублирования