@import em Zig
O @import é o builtin mais utilizado em Zig. Ele é responsável por importar módulos, pacotes externos e a biblioteca padrão para o escopo atual do código. Diferente de muitas linguagens, o Zig não possui um sistema de importação baseado em caminhos do sistema de arquivos global — tudo é resolvido relativamente ao arquivo atual ou configurado no sistema de build.
Sintaxe
@import(comptime path: []const u8) type
O que faz
O @import carrega e compila um arquivo-fonte Zig ou pacote, retornando a struct de nível superior (top-level struct) do módulo importado. Isso permite acessar todas as declarações públicas (pub) do módulo importado.
Quando chamado com o mesmo argumento múltiplas vezes no mesmo programa, o compilador não recompila o módulo — ele reutiliza o resultado da primeira compilação. Isso torna o @import idempotente e eficiente.
Parâmetros
- path (
[]const u8, comptime): Uma string literal conhecida em tempo de compilação que especifica o módulo a ser importado. Pode ser:- Um nome de pacote registrado no
build.zig(ex:"std","meu_pacote") - Um caminho relativo para um arquivo
.zig(ex:"./utils.zig","../lib/math.zig") - Um caminho para um arquivo C (ex:
"header.h"quando usado com@cImport)
- Um nome de pacote registrado no
Valor de retorno
Retorna um type — a struct implícita de nível superior do módulo importado. Através desse tipo, você pode acessar todas as declarações marcadas como pub no módulo.
Exemplos práticos
Exemplo 1: Importando a biblioteca padrão
const std = @import("std");
pub fn main() void {
const stdout = std.io.getStdOut().writer();
stdout.print("Olá, Zig!\n", .{}) catch {};
}
A importação de std é a forma mais comum de usar @import. A variável std recebe a struct do módulo da biblioteca padrão, permitindo acesso a std.io, std.mem, std.fmt e todos os outros submódulos.
Exemplo 2: Importando um módulo local
// arquivo: src/math_utils.zig
pub fn somar(a: i32, b: i32) i32 {
return a + b;
}
pub fn multiplicar(a: i32, b: i32) i32 {
return a * b;
}
const segredo_interno = 42; // não é pub, não será acessível
// arquivo: src/main.zig
const math = @import("math_utils.zig");
pub fn main() void {
const resultado = math.somar(10, 20);
const produto = math.multiplicar(5, 6);
const std = @import("std");
std.debug.print("Soma: {}, Produto: {}\n", .{ resultado, produto });
}
Note que segredo_interno não é acessível em main.zig porque não foi declarado como pub.
Exemplo 3: Importando pacotes do build.zig
// No build.zig, você registra um pacote:
// exe.root_module.addImport("config", config_module);
// Depois, no código-fonte:
const config = @import("config");
pub fn main() void {
const porta = config.PORTA_PADRAO;
const host = config.HOST;
// ...
}
Pacotes registrados no sistema de build são importados pelo nome, sem extensão .zig e sem caminho relativo.
Casos de uso comuns
Importar a biblioteca padrão: Praticamente todo programa Zig começa com
const std = @import("std");.Organizar código em módulos: Dividir código em vários arquivos e importar conforme necessário para manter o projeto organizado.
Usar dependências externas: Pacotes obtidos pelo gerenciador de pacotes do Zig (zon) são importados via nomes registrados no
build.zig.Importar arquivos de configuração: Usar
@importpara trazer constantes de configuração definidas em módulos separados.Testes: Em arquivos de teste, importar o módulo que está sendo testado para verificar seu comportamento.
const modulo = @import("meu_modulo.zig");
const std = @import("std");
const expect = std.testing.expect;
test "somar funciona corretamente" {
try expect(modulo.somar(2, 3) == 5);
}
Observações importantes
- O
@importsó aceita strings literais conhecidas em tempo de compilação. Não é possível construir o caminho dinamicamente em tempo de execução. - Caminhos relativos são resolvidos a partir do diretório do arquivo que contém o
@import. - Importações circulares são permitidas pelo compilador e resolvidas automaticamente.
- O resultado de
@importé tipicamente atribuído a uma constanteconstno escopo do arquivo.
Estrutura de módulos com @import
Uma prática comum é criar um arquivo root.zig ou lib.zig que re-exporta declarações de sub-módulos, criando uma API pública limpa para a biblioteca:
// src/lib.zig — ponto de entrada da biblioteca
pub const http = @import("http.zig");
pub const json = @import("json.zig");
pub const utils = @import("utils.zig");
// Usuário importa apenas lib.zig e tem acesso a tudo:
// const minha_lib = @import("minha_lib");
// minha_lib.http.get(...)
// minha_lib.json.parse(...)
Essa organização mantém a estrutura interna de arquivos flexível sem afetar os usuários da biblioteca.
@import("builtin") — informações de build
Um uso especial de @import é importar o módulo "builtin", que contém informações sobre o alvo de compilação:
const builtin = @import("builtin");
pub fn main() void {
// Verificar sistema operacional em comptime
if (builtin.os.tag == .linux) {
// Código específico para Linux
}
// Verificar modo de build
if (builtin.mode == .Debug) {
// Validações extras apenas em debug
}
// Verificar arquitetura
if (builtin.cpu.arch == .x86_64) {
// Otimizações específicas para x86_64
}
}
O módulo builtin permite escrever código que se adapta ao alvo de compilação sem dependências externas.
Idempotência do @import
O compilador Zig garante que cada módulo seja compilado apenas uma vez, mesmo que seja importado por múltiplos arquivos. Isso significa:
- Não há custo extra por importar o mesmo módulo em vários lugares.
- Declarações de tipos importados são idênticas —
@import("std").mem.Allocatoré sempre o mesmo tipo, independentemente de onde é importado. - Isso é fundamental para que tipos de diferentes módulos sejam compatíveis entre si.
Comparação com sistemas de módulos de outras linguagens
| Linguagem | Mecanismo | Avaliado em |
|---|---|---|
| C | #include (textual) | Pré-processamento |
| Python | import | Runtime |
| Rust | mod / use | Compilação |
| Zig | @import | Compilação |
Diferente do #include do C, que é uma substituição textual simples, o @import do Zig faz compilação real do módulo e retorna seu tipo. Diferente do Python, não há custo em runtime. A abordagem é similar ao Rust, mas integrada como builtin em vez de palavra-chave da linguagem.
Perguntas Frequentes
P: Posso usar @import dentro de uma função?
Sim, mas o resultado ainda deve ser comptime. O uso mais comum é no nível do arquivo, mas @import dentro de funções ou blocos comptime também é válido.
P: O que acontece com importações circulares?
Zig permite importações circulares. O compilador resolve as dependências circulares automaticamente. No entanto, o uso de tipos antes de sua declaração completa pode causar erros — organize os tipos para evitar dependências circulares de tipos quando possível.
P: Posso importar arquivos de outros diretórios usando ../?
Sim. Caminhos relativos com ../ funcionam e são resolvidos a partir do diretório do arquivo que contém o @import. Contudo, para projetos grandes, prefira registrar módulos no build.zig para evitar dependências de caminhos frágeis.
Builtins relacionados
- @cImport — Importa cabeçalhos C para uso em Zig
- @embedFile — Incorpora o conteúdo de um arquivo como dados binários
- @compileError — Gera erro de compilação personalizado