Lua 5.4 en 2026: introducción al lenguaje de scripting más embebido del mundo

Lua nació en 1993 en la Pontificia Universidad Católica de Río de Janeiro, de la mano de Roberto Ierusalimschy, Luiz Henrique de Figueiredo y Waldemar Celes. Treinta años después sigue siendo el lenguaje de scripting embebido por excelencia: ligero, rápido y diseñado desde el principio para vivir dentro de otras aplicaciones en lugar de ejecutarse solo. La versión 5.4, publicada en junio de 2020, es la rama estable actual y la que se usa como referencia en este artículo.

¿Qué tiene de especial Lua?

Lo primero que sorprende de Lua es su tamaño. La biblioteca estándar completa ocupa menos de 300 KB una vez compilada. Eso lo convierte en candidato natural para sistemas embebidos, motores de videojuegos o proxies de red donde el footprint importa. La VM de Lua es una máquina de registros escrita en C ANSI, lo que significa que se compila en prácticamente cualquier plataforma sin tocar una sola línea de código fuente.

El otro rasgo definitorio es la integración con C. La C API de Lua permite llamar funciones Lua desde C y funciones C desde Lua con muy poco código de pegamento. No hace falta un compilador especial ni un generador de bindings: la API es parte de la propia biblioteca. Si te interesa ese aspecto en profundidad, el artículo sobre la C API de Lua cubre todos los detalles.

Evolución del lenguaje: de 5.1 a 5.4

Conviene conocer el árbol genealógico de las versiones porque no todas son compatibles entre sí y muchas herramientas siguen ancladas en versiones antiguas:

  • Lua 5.1: la versión que inmortalizó LuaJIT. Aún muy presente porque LuaJIT la soporta y es la que usan Defold, OpenResty y otros entornos de producción.
  • Lua 5.2: introdujo goto, eliminó el operador módulo de entorno y cambió el modelo de entornos de función.
  • Lua 5.3: añadió enteros nativos (antes todo era double), operadores de bits y strings UTF-8 básicos.
  • Lua 5.4: la versión actual. Amplía los enteros a int64, introduce las to-be-closed variables con <toclose>, activa el recolector de basura generacional por defecto y mejora el depurador.

Tipos de datos

Lua tiene ocho tipos primitivos. A diferencia de otros lenguajes, todos son de primera clase: pueden almacenarse en variables, pasarse como argumentos y devolverse desde funciones.

  • nil: la ausencia de valor. Distinto de false.
  • boolean: true o false. En condiciones, nil y false son los únicos valores falsy; el resto (incluido 0) son truthy.
  • number: en Lua 5.4 puede ser integer (int64) o float (double). El literal 1 es integer; 1.0 es float.
  • string: inmutable, con codificación arbitraria de bytes. Internamente se deduplican (interning).
  • function: las funciones son ciudadanos de primera clase. Cierran sobre variables locales formando closures.
  • table: la única estructura de datos del lenguaje. Actúa como array, diccionario, objeto y módulo a la vez.
  • userdata: puntero a datos C gestionados por el host. Permite que la aplicación anfitriona exponga sus propios tipos.
  • thread: una corrutina. No es un hilo del sistema operativo; es una función que puede suspenderse y reanudarse cooperativamente.

Sintaxis básica

Lua usa palabras clave en inglés, bloques delimitados por do/end y comentarios con --. No hay punto y coma obligatorio; los saltos de línea y los espacios son equivalentes como separadores.

-- Variables locales (siempre preferibles a las globales)
local nombre = "Lua"
local version = 5.4
local activo = true

-- Múltiples asignaciones simultáneas
local a, b = 10, 20
a, b = b, a  -- intercambio sin variable temporal

-- Condicional
if version >= 5.4 then
    print("Integers nativos disponibles")
elseif version >= 5.3 then
    print("Integers pero sin to-be-closed")
else
    print("Versión antigua")
end

-- Bucle numérico (inicio, fin, paso)
for i = 1, 5 do
    io.write(i .. " ")
end
print()  -- salto de línea

-- Bucle genérico con ipairs (arrays)
local frutas = {"manzana", "pera", "naranja"}
for indice, valor in ipairs(frutas) do
    print(indice, valor)
end

Funciones como ciudadanos de primera clase

Las funciones en Lua admiten múltiples valores de retorno, algo que elimina la necesidad de punteros de salida o structs auxiliares habituales en C:

-- Múltiples valores de retorno
local function minmax(t)
    local min, max = t[1], t[1]
    for _, v in ipairs(t) do
        if v < min then min = v end
        if v > max then max = v end
    end
    return min, max
end

local valores = {3, 1, 4, 1, 5, 9, 2, 6}
local minimo, maximo = minmax(valores)
print(minimo, maximo)  -- 1  9

-- Varargs
local function suma(...)
    local total = 0
    for _, v in ipairs({...}) do
        total = total + v
    end
    return total
end
print(suma(1, 2, 3, 4, 5))  -- 15

-- Closure
local function contador(inicio)
    local n = inicio
    return function()
        n = n + 1
        return n
    end
end

local siguiente = contador(0)
print(siguiente())  -- 1
print(siguiente())  -- 2

To-be-closed variables: la novedad de Lua 5.4

Lua 5.4 introduce las to-be-closed variables, que permiten garantizar la liberación de recursos aunque ocurra un error. Se declaran con el atributo <toclose> y su metamétodo __close se llama automáticamente al salir del bloque:

local function abrirFichero(ruta)
    local f = io.open(ruta, "r")
    if not f then error("No se puede abrir: " .. ruta) end
    -- El metamétodo __close de los file handles cierra el fichero
    return f
end

do
    local f <toclose> = abrirFichero("datos.txt")
    local contenido = f:read("*a")
    print(#contenido .. " bytes leídos")
    -- f se cierra aquí automáticamente, incluso si print lanza un error
end

Dónde se usa Lua en 2026

El ecosistema de Lua abarca áreas muy dispares. En videojuegos, frameworks como LÖVE 2D y Defold usan Lua como lenguaje de scripting principal. En el lado del servidor, OpenResty extiende nginx con LuaJIT para procesar millones de peticiones por segundo. En el mundo de los editores, Neovim adoptó Lua como lenguaje oficial de configuración desde la versión 0.5. Y en dispositivos embebidos, microcontroladores como el ESP32 pueden ejecutar eLua o NodeMCU, ambos basados en Lua.

La razón por la que Lua sobrevive en todos estos nichos es la misma: una VM pequeña, una API de C limpia y un diseño que prioriza la simplicidad sobre la completitud. No intenta competir con Python en bibliotecas estándar ni con JavaScript en popularidad. Su ventaja es ser la pieza pequeña y eficiente que encaja donde los demás no caben.

Imagen: Pexels / Leonid Altman

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP