¿Por qué un guion bajo antes de import en Go? Lo analizamos con ejemplos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Qué se esconde detrás del guion bajo en import? Guía para quienes quieren entender Go más a fondo



¿Alguna vez has abierto código ajeno en Go y has visto una línea como import _ "github.com/lib/pq"? La primera reacción — «¿Y esto es legal? ¿El compilador no se quejará?». Spoiler: no se quejará. Es más, es una de las características más elegantes del lenguaje, que a menudo genera dudas en los principiantes. Analicemos qué hace realmente este misterioso símbolo de guion bajo antes de la importación en Go, y cómo usarlo con inteligencia.



En Stack Overflow, esta pregunta ha acumulado miles de visitas, y no es de extrañar. La sintaxis parece un error tipográfico, pero detrás hay un concepto profundo de inicialización de paquetes y efectos secundarios. En este artículo no solo daremos una definición seca, sino que mostraremos escenarios reales donde no se puede prescindir de _. ¡Allá vamos!



El problema: «Importé, pero no lo uso»



En Go hay una regla estricta: cada paquete importado debe ser utilizado. De lo contrario, el código no se compilará. Esto se hace por limpieza y rendimiento — sin carga muerta. Pero, ¿qué hacer si necesitamos simplemente ejecutar el código de un paquete al iniciar el programa, sin llamar directamente a sus funciones? Aquí es donde viene al rescate el blank identifier (identificador vacío) — el símbolo _.



Cuando escribes:

import _ "some/package"

— le dices al compilador: «Sé que no llamaré a funciones de este paquete, pero, por favor, ejecuta su función init()». Go realizará la inicialización, pero no dará el error «unused import».



Cómo funciona init() y por qué es importante



Cada paquete en Go puede contener una o varias funciones init(). Se ejecutan automáticamente al importar el paquete, antes de la función main(). El orden de ejecución está estrictamente regulado:

  • Primero se inicializan las variables del paquete
  • Luego se llama a init()
  • Si el paquete importa otros paquetes, estos se inicializan antes


Ejemplo para mayor claridad. Creemos un paquete config:

// config/config.gopackage config

var AppName string

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


Ahora en main.go podemos importar este paquete «en silencio»:

// main.gopackage main

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

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


Resultado de la ejecución:

Config initialized: MyApp v1.0Main function started


Observa: no llamamos a config.AppName — simplemente importamos el paquete con guion bajo, y su init() se ejecutó automáticamente. Esta es la mecánica básica sobre la que se construyen muchas soluciones reales.



El escenario principal: registro de controladores de bases de datos



El ejemplo más común del uso de import _ en código de producción es el registro de controladores de bases de datos. El paquete estándar database/sql utiliza el patrón «fábrica»: todos los controladores se registran mediante sql.Register() en sus funciones init().



Mira cómo se conecta PostgreSQL (controlador lib/pq):

import (    "database/sql"    _ "github.com/lib/pq" // registra el controlador "postgres")

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


Internamente, en lib/pq, dentro de init() hay algo como:

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


Sin la importación vacía, el controlador no se habría registrado, y sql.Open("postgres", ...) devolvería un error «unknown driver». Lo mismo funciona para MySQL, SQLite, Oracle y otras bases de datos.

Blogs

Book Recommendations