Por que o underscore antes de import em Go? Analisando com exemplos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O que está por trás do sublinhado no import? Guia para quem quer entender Go mais a fundo



Você já abriu um código Go de outra pessoa e viu uma linha como import _ "github.com/lib/pq"? A primeira reação — "Isso é legal? O compilador não vai reclamar?". Spoiler: não vai reclamar. Mais do que isso, é um dos recursos mais elegantes da linguagem, que frequentemente gera dúvidas em iniciantes. Vamos entender o que esse misterioso símbolo de sublinhado antes do import realmente faz em Go e como usá-lo com sabedoria.



No Stack Overflow, essa pergunta acumulou milhares de visualizações — e não é surpresa. A sintaxe parece um erro de digitação, mas por trás dela existe um conceito profundo de inicialização de pacotes e efeitos colaterais. Neste artigo, não vamos apenas dar uma definição seca, mas mostrar cenários reais onde o _ é indispensável. Vamos lá!



Problema: "Importei, mas não uso"



Em Go, existe uma regra rigorosa: cada pacote importado deve ser usado. Caso contrário, o código não compila. Isso foi feito para garantir limpeza e desempenho — sem peso morto. Mas o que fazer quando precisamos apenas executar o código de um pacote na inicialização do programa, sem chamar suas funções diretamente? É aí que entra o blank identifier (identificador vazio) — o símbolo _.



Quando você escreve:

import _ "some/package"

— você está dizendo ao compilador: "Eu sei que não vou chamar funções deste pacote, mas, por favor, execute sua função init()". O Go realiza a inicialização, mas sem gerar o erro "unused import".



Como funciona o init() e por que é importante



Cada pacote em Go pode conter uma ou mais funções init(). Elas são executadas automaticamente quando o pacote é importado, antes da função main(). A ordem de execução é estritamente definida:

  • Primeiro, as variáveis do pacote são inicializadas
  • Em seguida, init() é chamada
  • Se o pacote importa outros pacotes — eles são inicializados antes


Exemplo para ilustrar. Vamos criar um pacote config:

// config/config.gopackage config

var AppName string

func init() { AppName = "MyApp v1.0" println("Config initialized:", AppName)}


Agora, no main.go, podemos importar este pacote "silenciosamente":

// main.gopackage main

import ( _ "example.com/project/config" "fmt")

func main() { fmt.Println("Main function started")}


Resultado da execução:

Config initialized: MyApp v1.0Main function started


Observe: não chamamos config.AppName — apenas importamos o pacote com sublinhado, e seu init() foi executado automaticamente. Essa é a mecânica básica sobre a qual muitas soluções reais são construídas.



Cenário principal: registro de drivers de banco de dados



O exemplo mais comum de uso de import _ em código de produção é o registro de drivers de banco de dados. O pacote padrão database/sql usa o padrão "fábrica": todos os drivers são registrados através de sql.Register() em suas funções init().



Veja como o PostgreSQL é conectado (driver lib/pq):

import (    "database/sql"    _ "github.com/lib/pq" // registra o driver "postgres")

func main() { db, err := sql.Open("postgres", "user=... dbname=...") // ...}


Internamente, no lib/pq, dentro do init() há algo como:

func init() {    sql.Register("postgres", &Driver{})}


Sem a importação vazia, o driver não seria registrado, e sql.Open("postgres", ...) retornaria um erro "unknown driver". O mesmo funciona para MySQL, SQLite, Oracle e outros bancos de dados.

Blogs

Book Recommendations