Когда ты приходишь в Lua после Python или JS и узнаёшь, что тут нет ни массивов, ни объектов, ни словарей… А есть только таблицы.
Твоё лицо: 😐
Лицо через неделю, когда понял мощь таблиц: 🤯
Давайте разбираться, почему таблицы в Lua — это не «бедность языка», а гениальный дизайн, который новички упорно недооценивают.

Подождите, тут правда только таблицы?
Да. Одна структура данных на все случаи жизни. Хочешь массив? Таблица. Словарь? Таблица. Объект с методами? Угадал — таблица. Очередь, стек, граф, дерево? Всё таблицы.
Lua-разработчик, объясняющий архитектуру проекта:
— А вот тут у нас таблица таблиц, которая хранит таблицы с таблицами.
— Понятно, спасибо.
Звучит как безумие, но на практике это невероятно элегантно.
Таблица как массив: индексация с единицы (да, с ЕДИНИЦЫ)
Вот первый шок для любого, кто пришёл из мира нормальных языков:
local fruits = {"яблоко", "банан", "вишня"}
print(fruits[1]) -- "яблоко"
print(fruits[0]) -- nil 😭Индексация с 1. Не с нуля. С ЕДИНИЦЫ.
Каждый бывший C-шник в этот момент чувствует физическую боль. Но у Lua есть свои причины — язык проектировался для людей, далёких от программирования (инженеры, дизайнеры), и для них «первый элемент = 1» — это логично.
Привыкаешь за пару дней. Страдаешь — первые два часа.
Полезная классика: перебор массива
local heroes = {"Lua", "Python", "JavaScript"}
for i, name in ipairs(heroes) do
print(i .. ". " .. name)
end
-- 1. Lua
-- 2. Python
-- 3. JavaScriptipairs — обходит по числовым индексам по порядку. Запомни его, он будет везде.
Таблица как словарь: ключ-значение на стероидах
Вот тут начинается магия. Таблица может одновременно быть массивом и словарём:
local player = {
name = "ProGamer228",
level = 42,
hp = 100,
"скрытое достижение" -- это индекс [1]
}
print(player.name) -- "ProGamer228"
print(player["level"]) -- 42
print(player[1]) -- "скрытое достижение"Две формы доступа: через точку (player.name) и через скобки (player["name"]). Скобки нужны, когда ключ — переменная или содержит спецсимволы.
local key = "hp"
print(player[key]) -- 100
-- Ключом может быть вообще что угодно
local weird = {}
weird[true] = "да"
weird[3.14] = "пи"
weird[print] = "функция как ключ, почему бы и нет"Ключом может быть любой тип, кроме nil. Это открывает безумные возможности, но злоупотреблять не стоит — читаемость кода тоже важна.
Длина таблицы: тут всё сложно
Оператор # возвращает длину «массивной» части таблицы:
local t = {10, 20, 30, 40}
print(#t) -- 4 ✅
local mixed = {10, 20, nil, 40}
print(#mixed) -- может быть 2, а может быть 4 🤡Важное правило: если в массивной части есть nil — поведение # не определено. Lua может вернуть любое число. Это не баг, это by design. Просто не оставляй дырки в массивах.
Хочешь надёжную длину для словарей? Считай вручную:
local function tableLength(t)
local count = 0
for _ in pairs(t) do
count = count + 1
end
return count
endТаблица как объект: ООП по-луашному
В Lua нет классов. Но кого это останавливало?
local Dog = {}
Dog.__index = Dog
function Dog.new(name, breed)
local self = setmetatable({}, Dog)
self.name = name
self.breed = breed
return self
end
function Dog:bark()
print(self.name .. " говорит: ГАВ!")
end
function Dog:info()
print(self.name .. " — порода: " .. self.breed)
end
local rex = Dog.new("Рекс", "Овчарка")
rex:bark() -- Рекс говорит: ГАВ!
rex:info() -- Рекс — порода: ОвчаркаОбрати внимание на двоеточие (:) вместо точки при вызове метода — это синтаксический сахар, который автоматически передаёт объект как self. Без этого пришлось бы писать rex.bark(rex), и это выглядело бы грустно.
Наследование? Тоже через таблицы
local Puppy = setmetatable({}, {__index = Dog})
Puppy.__index = Puppy
function Puppy.new(name, breed, toy)
local self = Dog.new(name, breed)
setmetatable(self, Puppy)
self.toy = toy
return self
end
function Puppy:play()
print(self.name .. " играет с " .. self.toy .. "!")
end
local baby = Puppy.new("Бобик", "Корги", "мячиком")
baby:bark() -- Бобик говорит: ГАВ! (унаследовано от Dog)
baby:play() -- Бобик играет с мячиком!Метатаблицы и __index — это сердце объектной системы Lua. Когда поле не найдено в текущей таблице, Lua заглядывает в __index метатаблицы. Цепочка может быть сколько угодно длинной — вот тебе и наследование.

Метаметоды: перегружаем вообще всё
Метатаблицы позволяют переопределить поведение таблицы для стандартных операций:
local Vector = {}
Vector.__index = Vector
function Vector.new(x, y)
return setmetatable({x = x, y = y}, Vector)
end
-- Перегружаем оператор сложения
function Vector.__add(a, b)
return Vector.new(a.x + b.x, a.y + b.y)
end
-- Перегружаем вывод через tostring
function Vector.__tostring(v)
return "(" .. v.x .. ", " .. v.y .. ")"
end
local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)
local v3 = v1 + v2
print(tostring(v3)) -- (4, 6)Основные метаметоды, которые стоит знать:
__add,__sub,__mul,__div— арифметика__eq,__lt,__le— сравнение__tostring— строковое представление__index— доступ к несуществующему полю__newindex— запись нового поля__call— вызов таблицы как функции__len— переопределение оператора#
С __call можно сделать таблицу вызываемой — и это реально используется в продакшене:
local Logger = setmetatable({}, {
__call = function(self, message)
print("[LOG] " .. os.date("%H:%M:%S") .. " — " .. message)
end
})
Logger("Сервер запущен") -- [LOG] 14:32:01 — Сервер запущенПрактические паттерны: таблицы в реальном коде
Конфиг через таблицу
local config = {
server = {
host = "localhost",
port = 8080,
},
database = {
name = "myapp",
user = "admin",
password = "hunter2", -- классика
},
debug = true,
}
print(config.server.port) -- 8080Множество (Set)
local function Set(list)
local set = {}
for _, v in ipairs(list) do
set[v] = true
end
return set
end
local langs = Set{"Lua", "Python", "JavaScript", "Lua"}
if langs["Lua"] then
print("Lua в деле!") -- Lua в деле!
end
if not langs["COBOL"] then
print("COBOL? Не, не слышали")
endОчередь
local Queue = {}
Queue.__index = Queue
function Queue.new()
return setmetatable({first = 1, last = 0}, Queue)
end
function Queue:push(value)
self.last = self.last + 1
self[self.last] = value
end
function Queue:pop()
if self.first > self.last then return nil end
local value = self[self.first]
self[self.first] = nil
self.first = self.first + 1
return value
end
local q = Queue.new()
q:push("первый")
q:push("второй")
print(q:pop()) -- "первый"
print(q:pop()) -- "второй"Топ ошибок новичков с таблицами
1. Забыть, что таблицы передаются по ссылке
local a = {1, 2, 3}
local b = a -- b указывает на ту же таблицу!
b[1] = 999
print(a[1]) -- 999 (сюрприз!)Хочешь копию? Делай вручную:
local function shallowCopy(t)
local copy = {}
for k, v in pairs(t) do
copy[k] = v
end
return copy
end2. Путать pairs и ipairs
ipairs— только числовые ключи, по порядку, останавливается на первомnilpairs— все ключи, порядок не гарантирован
3. Изменять таблицу во время итерации по ней
Не делай так. Просто не делай. Собирай изменения в отдельную таблицу, а потом применяй.
4. Считать # надёжным для таблиц с дырками
Уже говорили, но повторим: nil в середине массива = непредсказуемая длина. Без вариантов.
Изучай Lua и другие языки вместе с Кодик!
Если ты дочитал до этого места — тебе явно интересна не только теория, но и практика. Таблицы в Lua лучше всего усваиваются, когда ты пишешь код руками, а не просто читаешь статьи.
Кодик — это приложение, в котором можно изучать программирование с нуля и прокачивать навыки через практические задания. Python, JavaScript, HTML, CSS и другие технологии — всё с упором на то, чтобы ты реально писал код, а не просто смотрел на него.
А ещё у нас есть Telegram-канал с сообществом из 2000+ разработчиков, где регулярно выходят полезные посты, разборы задач и мемы про программирование (без них никуда). Отличный способ повторять материал в удобном месте — в очереди, в транспорте, за обедом. Подписывайся и прокачивайся каждый день.
Итого: почему таблицы — это гениально?
Таблицы в Lua — это швейцарский нож, который сначала кажется странным, а потом ты не понимаешь, как жил без него. Одна структура данных вместо десяти, минимум синтаксиса, максимум гибкости.
Вот что стоит запомнить:
Таблицы заменяют массивы, словари, объекты, классы и вообще всё
Индексация с 1 — не баг, а фича (просто прими это)
Метатаблицы превращают таблицы в полноценную объектную систему
Таблицы передаются по ссылке — будь внимателен с копированием
#работает надёжно только для массивов без дырок
Lua — это язык, который доказывает: меньше — значит больше. И таблицы — лучший пример этой философии.
Теперь иди и напиши что-нибудь на Lua. Таблицы ждут. 🚀
