Lua — это лёгкий, быстрый и гибкий скриптовый язык, который появился в 1993 году. Он написан на C и чаще всего используется не как самостоятельный язык, а как встраиваемый инструмент для других приложений.

Если вы играли в World of Warcraft и устанавливали аддоны, вы уже сталкивались с Lua. Redis исполняет Lua-скрипты внутри себя. Nginx использует его для обработки HTTP-запросов. В NeoVim плагины тоже можно писать на Lua. Короче говоря — язык не из популярных топов, но крайне полезен и встраиваем во многие инфраструктурные решения.

Синтаксис Lua: просто и понятно

Начнём с классики:

print("Hello, World!")

Синтаксис Lua немного напоминает Pascal и Python: конструкции завершаются end, типы выводятся автоматически, всё просто и компактно — идеально для скриптов и прототипов.

В языке есть локальные и глобальные переменные:

  • Локальные объявляются через local и живут только в пределах текущего блока.

  • Глобальные — по умолчанию. Они сохраняются в специальной таблице _G, доступной из любого куска кода, работающего в той же среде выполнения.

Пример чуть более практичный — задача из Advent of Code 2022, день 1:
Найти максимальную сумму чисел, сгруппированных по блокам, разделённым пустыми строками.

local max, current = 0, 0
for line in io.lines("input.txt") do
    if line == "" then
        if current > max then
            max = current
        end
        current = 0
    else
        current = current + tonumber(line)
    end
end

if current > max then
    max = current
end

print(max)

Типы данных в Lua

В Lua всего восемь базовых типов:

number, string, boolean, nil, function, table, thread, userdata

Несколько нюансов:

  • number объединяет и целые, и дробные числа.

  • string — обычные строки, являются иммутабельными.

  • nil — Null, Nil, None называй как хочешь.

  • function — функции — это значения первого класса: их можно присваивать переменным, передавать как аргументы и возвращать из других функций.

  • table — универсальная структура данных: массив (индексируются с 1), словарь и объект — всё в одном.

  • thread — корутины.

  • userdata — интерфейс для взаимодействия с C-данными.

Типизация динамическая и слабая: переменные могут менять тип на лету, а Lua часто пытается «угадать» вашу задумку. Например, строку "42" можно неявно превратить в число. А если все таки хочется явного приведения типов, то существуют встроенные функции для такого: например tonumber(n) или tostring(s).

Встраиваемость: главная сила Lua

Lua удобно встроить в любую программу на C или даже Go. Интерпретатор предоставляет простой C API, через который можно выполнять скрипты, обмениваться данными и расширять язык новыми функциями.

Пример: как исполнить Lua-скрипт из Go

Предположим есть Lua-файл fib5.lua который считает число Фибоначчи и возвращает результат для числа 5.

function fib(n)
    local fib = {1, 1} 
    for i = 3,n,1 do
        fib[#fib+1] = fib[#fib] + fib[#fib-1]
    end
    return fib[n]
end
return fib(5)

Программа на Go:

Я буду использовать пакет github.com/yuin/gopher-lua который предоставляет простой интерфейс взаимодействия, но при желании можно обойтись и без лишних зависимостей с помощью CGO.

package main

import (
	"fmt"

	lua "github.com/yuin/gopher-lua"
)

func main() {
	var l *lua.LState = lua.NewState()
	defer l.Close()
	if err := l.DoFile("fib5.lua"); err != nil {
		panic(err)
	}

	// достаем результат выполнения с вершины стэка
	result := l.Get(-1) // result типа lua.LValue
	// и очищаем его
	l.Pop(1)
	fmt.Printf("result: %d\n", result) // result: 5
}

Eсли мы захотим заново выполнить подсчет, то нам не надо заново загружать файл, функция fib(n) уже лежит в глобальных переменных нашего l *lua.LState Нужно лишь достать её оттуда и положить на стэк вместе с аргументами, а затем вызвать l.Call(nargs, nret) для получения результата.

...
//складываем на стэк функцию из глобальных переменных
l.Push(l.GetGlobal("fib")) // stack: function "fib"
//складываем на стэк аргумент функции
l.Push(lua.LNumber(15))    // stack: function "fib", 15
// l.Call забирает из стэка функцию и 1 аргумент, и складывает обратно 1 результат
l.Call(1, 1)               // stack: 610
result = l.Get(-1)         // stack: 610
l.Pop(1)                   // stack:
fmt.Printf("result: %d\n", result) //result: 610

Небольшое уточнение: не примитивные типы используемые в 2 языках сильно отличаются (особенно массивы), поэтому часто требуется нетривиальные преобразования чтобы получить нормальный результат.

Где Lua уже используется

Lua активно применяется в реальных проектах:

  • World of Warcraft — аддоны и интерфейс.

  • Redis — атомарные операции в скриптах.

  • Nginx — модуль ngx_http_lua_module.

  • NeoVim — современный API для плагинов.

  • Игровые движки — Defold, Pico-8, Love2D и другие.

Зачем учить Lua в 2025 году?

Lua — не универсальный язык на все случаи жизни. Но если вы:

  • пишете backend на Nginx или Redis,

  • работаете с NeoVim и хотите делать плагины,

  • или просто хотите встроить скриптовый язык в своё приложение,

... то Lua — это идеальный инструмент. Он маленький, быстрый и гибкий. И к тому же — приятно удивляет своей лаконичностью.