@import em Zig — Referência e Exemplos

@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)

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

  1. Importar a biblioteca padrão: Praticamente todo programa Zig começa com const std = @import("std");.

  2. Organizar código em módulos: Dividir código em vários arquivos e importar conforme necessário para manter o projeto organizado.

  3. Usar dependências externas: Pacotes obtidos pelo gerenciador de pacotes do Zig (zon) são importados via nomes registrados no build.zig.

  4. Importar arquivos de configuração: Usar @import para trazer constantes de configuração definidas em módulos separados.

  5. 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 @import só 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 constante const no 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

LinguagemMecanismoAvaliado em
C#include (textual)Pré-processamento
PythonimportRuntime
Rustmod / useCompilação
Zig@importCompilaçã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

Tutoriais relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.