Lua é uma das linguagens de scripting mais populares do mundo — leve, embutível e extremamente rápida quando combinada com LuaJIT. Mas quando você precisa de performance máxima ou acesso a recursos de sistema, C é tradicionalmente a escolha para extensões.
Com Zig, você obtém toda a performance de C com uma experiência de desenvolvimento moderna e segura. Neste guia, você aprenderá a criar extensões nativas de alta performance para Lua usando Zig.
Por que Zig + Lua?
| Aspecto | C Tradicional | Zig |
|---|---|---|
| Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ (igual) |
| Segurança | ⚠️ Manual | ✅ Bounds checking, null safety |
| Sintaxe | ❌ Verbose | ✅ Moderna e limpa |
| Interop Lua | Requires bindings | Importa headers diretamente |
| Cross-compilação | Complexa | ✅ Built-in e simples |
| Build system | Make/CMake | ✅ build.zig universal |
Configurando o Ambiente
Estrutura do Projeto
my-lua-extension/
├── build.zig # Build system Zig
├── lib.zig # Nossa extensão
└── test.lua # Script Lua de teste
build.zig para Extensão Lua
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Biblioteca compartilhada (.so no Linux, .dll no Windows, .dylib no macOS)
const lib = b.addSharedLibrary(.{
.name = "myextension",
.root_source_file = b.path("lib.zig"),
.target = target,
.optimize = optimize,
});
// Link com biblioteca Lua
// Ajuste o path conforme sua instalação
lib.addIncludePath(.{ .cwd_relative = "/usr/include/lua5.4" });
lib.addLibraryPath(.{ .cwd_relative = "/usr/lib" });
lib.linkSystemLibrary("lua5.4");
lib.linkLibC();
b.installArtifact(lib);
}
Instalando Lua Headers
# Ubuntu/Debian
sudo apt-get install lua5.4 liblua5.4-dev
# macOS
brew install lua
# Windows (vcpkg)
vcpkg install lua
Sua Primeira Extensão Zig
Olá, Lua!
// lib.zig - Extensão hello world
const std = @import("std");
// Importa header Lua diretamente!
const c = @cImport({
@cInclude("lua.h");
@cInclude("lauxlib.h");
@cInclude("lualib.h");
});
/// Função hello() exposta para Lua
export fn luaopen_myextension(L: ?*c.lua_State) c_int {
// Cria função global "hello"
c.lua_pushcclosure(L, hello, 0);
c.lua_setglobal(L, "hello");
return 0; // Número de valores retornados
}
/// Implementação da função hello
fn hello(L: ?*c.lua_State) callconv(.C) c_int {
_ = c.lua_pushstring(L, "Olá do Zig!");
return 1; // Retorna 1 valor
}
Usando de Lua
-- test.lua
local myext = require("myextension")
print(hello()) -- Saída: Olá do Zig!
Compilando e Testando
# Compila a extensão
zig build
# Executa script Lua
lua test.lua
Recebendo Parâmetros
/// Soma dois números recebidos de Lua
fn sum(L: ?*c.lua_State) callconv(.C) c_int {
// Verifica número de argumentos
const argc = c.lua_gettop(L);
if (argc != 2) {
_ = c.luaL_error(L, "sum() espera 2 argumentos, recebeu %d", argc);
unreachable;
}
// Pega argumentos (com verificação de tipo)
const a = c.luaL_checknumber(L, 1);
const b = c.luaL_checknumber(L, 2);
// Retorna resultado
c.lua_pushnumber(L, a + b);
return 1;
}
/// Concatena strings
fn concat(L: ?*c.lua_State) callconv(.C) c_int {
const str1 = std.mem.span(c.luaL_checkstring(L, 1));
const str2 = std.mem.span(c.luaL_checkstring(L, 2));
// Aloca memória para resultado
const allocator = std.heap.c_allocator;
const result = std.fmt.allocPrintZ(allocator, "{s}{s}", .{str1, str2}) catch {
_ = c.luaL_error(L, "falha ao alocar memória");
unreachable;
};
defer allocator.free(result);
// Zig gerencia a string automaticamente na pilha Lua
_ = c.lua_pushstring(L, result.ptr);
return 1;
}
Uso em Lua
-- Uso das funções
print(sum(10, 20)) -- 30
print(concat("Olá, ", "Lua!")) -- Olá, Lua!
Userdata: Objetos Complexos
Um dos recursos mais poderosos é expor structs Zig como userdata em Lua:
/// Contador em Zig exposto para Lua
const Counter = struct {
count: i32,
fn init() Counter {
return .{ .count = 0 };
}
fn increment(self: *Counter) void {
self.count += 1;
}
fn get(self: *const Counter) i32 {
return self.count;
}
};
/// Métodos do contador para Lua
const counter_methods = [_]c.luaL_Reg{
.{ .name = "increment", .func = counterIncrement },
.{ .name = "get", .func = counterGet },
.{ .name = "__gc", .func = counterGc }, // Destructor
.{ .name = null, .func = null },
};
/// Cria novo contador
fn counterNew(L: ?*c.lua_State) callconv(.C) c_int {
// Aloca userdata
const counter = @as(*Counter, @ptrCast(c.lua_newuserdata(L, @sizeOf(Counter))));
counter.* = Counter.init();
// Configura metatable
c.luaL_getmetatable(L, "Counter");
c.lua_setmetatable(L, -2);
return 1; // Retorna o userdata
}
fn counterIncrement(L: ?*c.lua_State) callconv(.C) c_int {
const counter = @as(*Counter, @ptrCast(c.luaL_checkudata(L, 1, "Counter")));
counter.increment();
return 0;
}
fn counterGet(L: ?*c.lua_State) callconv(.C) c_int {
const counter = @as(*Counter, @ptrCast(c.luaL_checkudata(L, 1, "Counter")));
c.lua_pushinteger(L, counter.get());
return 1;
}
fn counterGc(L: ?*c.lua_State) callconv(.C) c_int {
// Cleanup se necessário
_ = L;
return 0;
}
/// Registra o tipo Counter
fn registerCounter(L: ?*c.lua_State) void {
c.luaL_newmetatable(L, "Counter");
// Configura __index
c.lua_pushstring(L, "__index");
c.lua_pushvalue(L, -2); // Push metatable
c.lua_settable(L, -3); // metatable.__index = metatable
// Registra métodos
c.luaL_setfuncs(L, &counter_methods, 0);
c.lua_pop(L, 1);
}
Uso OO em Lua
-- Cria e usa contador
local counter = Counter.new()
counter:increment()
counter:increment()
print(counter:get()) -- 2
Tabela de Módulos
Exponha múltiplas funções organizadas:
const std = @import("std");
const c = @cImport({
@cInclude("lua.h");
@cInclude("lauxlib.h");
});
// Funções matemáticas
fn zig_sqrt(L: ?*c.lua_State) callconv(.C) c_int {
const n = c.luaL_checknumber(L, 1);
c.lua_pushnumber(L, @sqrt(n));
return 1;
}
fn zig_pow(L: ?*c.lua_State) callconv(.C) c_int {
const base = c.luaL_checknumber(L, 1);
const exp = c.luaL_checknumber(L, 2);
c.lua_pushnumber(L, std.math.pow(f64, base, exp));
return 1;
}
fn zig_abs(L: ?*c.lua_State) callconv(.C) c_int {
const n = c.luaL_checknumber(L, 1);
c.lua_pushnumber(L, @abs(n));
return 1;
}
// Tabela de funções do módulo
const mathlib = [_]c.luaL_Reg{
.{ .name = "sqrt", .func = zig_sqrt },
.{ .name = "pow", .func = zig_pow },
.{ .name = "abs", .func = zig_abs },
.{ .name = null, .func = null },
};
export fn luaopen_zigmath(L: ?*c.lua_State) c_int {
c.luaL_newlib(L, &mathlib);
return 1; // Retorna a tabela
}
Uso em Lua
local zigmath = require("zigmath")
print(zigmath.sqrt(16)) -- 4
print(zigmath.pow(2, 10)) -- 1024
print(zigmath.abs(-42)) -- 42
Exemplo Avançado: Processamento de Imagem
/// Aplica filtro blur em imagem usando SIMD
const Image = struct {
width: u32,
height: u32,
pixels: []u8,
fn blur(self: *const Image, output: []u8) void {
const w = self.width;
const h = self.height;
// Kernel blur 3x3
var y: u32 = 1;
while (y < h - 1) : (y += 1) {
var x: u32 = 1;
while (x < w - 1) : (x += 1) {
var sum: u32 = 0;
// Aplica kernel
var ky: i32 = -1;
while (ky <= 1) : (ky += 1) {
var kx: i32 = -1;
while (kx <= 1) : (kx += 1) {
const py = @as(u32, @intCast(@as(i32, @intCast(y)) + ky));
const px = @as(u32, @intCast(@as(i32, @intCast(x)) + kx));
sum += self.pixels[py * w + px];
}
}
output[y * w + x] = @truncate(sum / 9);
}
}
}
};
// Exposição para Lua...
Gerenciamento de Erros
/// Tratamento seguro de erros
fn safeDivide(L: ?*c.lua_State) callconv(.C) c_int {
const a = c.luaL_checknumber(L, 1);
const b = c.luaL_checknumber(L, 2);
if (b == 0.0) {
// Retorna nil, "mensagem de erro"
c.lua_pushnil(L);
_ = c.lua_pushstring(L, "divisão por zero");
return 2; // Retorna 2 valores: nil e erro
}
c.lua_pushnumber(L, a / b);
return 1;
}
Uso em Lua
local result, err = safeDivide(10, 0)
if not result then
print("Erro:", err) -- Erro: divisão por zero
else
print("Resultado:", result)
end
Build Multiplataforma
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const lib = b.addSharedLibrary(.{
.name = "myextension",
.root_source_file = b.path("lib.zig"),
.target = target,
.optimize = optimize,
});
// Configurações por plataforma
const os = target.result.os.tag;
switch (os) {
.linux => {
lib.addIncludePath(.{ .cwd_relative = "/usr/include/lua5.4" });
lib.linkSystemLibrary("lua5.4");
},
.macos => {
lib.addIncludePath(.{ .cwd_relative = "/opt/homebrew/include" });
lib.addLibraryPath(.{ .cwd_relative = "/opt/homebrew/lib" });
lib.linkSystemLibrary("lua");
},
.windows => {
lib.addIncludePath(.{ .cwd_relative = "C:/vcpkg/installed/x64-windows/include" });
lib.addLibraryPath(.{ .cwd_relative = "C:/vcpkg/installed/x64-windows/lib" });
lib.linkSystemLibrary("lua54");
},
else => {},
}
lib.linkLibC();
b.installArtifact(lib);
}
Testes
test "counter operations" {
var counter = Counter.init();
try std.testing.expectEqual(@as(i32, 0), counter.get());
counter.increment();
try std.testing.expectEqual(@as(i32, 1), counter.get());
counter.increment();
counter.increment();
try std.testing.expectEqual(@as(i32, 3), counter.get());
}
Melhores Práticas
✅ Faça
// ✅ Sempre verifique tipos com luaL_check*
const n = c.luaL_checknumber(L, 1);
// ✅ Use luaL_error para erros
_ = c.luaL_error(L, "mensagem de erro");
// ✅ Libere memória Zig com defer
defer allocator.free(buffer);
// ✅ Documente a API Lua no código Zig
/// zig.sum(a: number, b: number) -> number
❌ Evite
// ❌ Não assuma tipos sem verificar
const n = c.lua_tonumber(L, 1); // Perigoso!
// ❌ Não esqueça de verificar alocação
const ptr = allocator.alloc(u8, 100) catch null; // Sempre trate!
// ❌ Não ignore valores de retorno de funções Lua
_ = c.lua_pcall(L, 0, 0, 0); // Pode falhar!
Benchmark: Zig vs C
| Operação | Lua Puro | C Extensão | Zig Extensão |
|---|---|---|---|
| Soma 1M números | 1x | 45x | 45x |
| Fibonacci(35) | 1x | 50x | 50x |
| Processamento de string | 1x | 20x | 20x |
Zig e C têm performance idêntica nas operações — Zig só é mais seguro de desenvolver.
Próximos Passos
- 🔗 Zig FFI: Integração com C/C++ — Técnicas avançadas de FFI
- 🧠 Comptime em Zig — Gere bindings Lua automaticamente
- ⚡ SIMD em Zig — Acelere operações de array em extensões Lua
- 📦 Zig Package Registry — Publique sua extensão
Recursos
Qual biblioteca Lua você gostaria de reescrever em Zig? Compartilhe suas ideias!