Ciclos de Importación, Importaciones Vacías y Dot Imports
Go prohíbe los ciclos de importación en tiempo de compilación.
Busca en todas las páginas de la documentación
Go prohíbe los ciclos de importación en tiempo de compilación.
Las importaciones vacías ejecutan la función init del paquete para efectos secundarios de registro, mientras que las dot imports fusionan los nombres exportados en el espacio de nombres del importador y se desaconsejan en el código de producción.
Un ciclo de importación ocurre cuando el paquete A importa B y B importa A (directa o indirectamente a través de una cadena).
El compilador rechaza los ciclos porque el orden de inicialización sería indefinido.
Las importaciones vacías (import _ "pkg") son solo para efectos secundarios.
Las dot imports (import . "pkg") exponen identificadores exportados sin un calificador y perjudican la legibilidad.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
package main
import (
"fmt"
_ "image/png" // registra el decodificador PNG a través de init
)
func main() {
fmt.Println("decodificadores registrados")
}Cuándo usar esto:
init.Rompiendo un ciclo con un paquete de contratos compartido:
example.com/app/
service/service.go
store/store.go
contract/contract.go
// contract/contract.go - sin importaciones de service o store
package contract
type User struct {
ID string
}
type Repository interface {
Save(u User) error
}// store/store.go
package store
import "example.com/app/contract"
type Memory struct{}
func (Memory) Save(u contract.User) error { return nil }// service/service.go
package service
import (
"example.com/app/contract"
"example.com/app/store"
)
func Run() {
var repo contract.Repository = store.Memory{}
_ = repo.Save(contract.User{ID: "1"})
}Lo que esto demuestra:
service y store dependen de contract, no el uno del otro.contract).service.Ciclos de importación: El compilador construye un DAG de paquetes.
Cualquier arista de retroceso falla la compilación con import cycle not allowed.
Refactoriza moviendo tipos, interfaces o funciones compartidas a una capa inferior que ambos lados importan.
Importaciones vacías: El compilador aún enlaza el paquete y ejecuta todas las funciones init en orden de dependencia antes de main.
Usos típicos:
database/sqlimage/*Dot imports: import . "fmt" te permite escribir Println en lugar de fmt.Println.
El estilo de Go rechaza esto en el código de aplicación porque grep y los lectores pierden el contexto del paquete.
| Patrón | Mover qué | Contrapartida |
|---|---|---|
| Extraer interfaces | Tipos de API orientados al consumidor | Archivo de paquete adicional |
| Extraer DTOs | Estructuras compartidas | Puede ampliar la superficie de la API |
| Inyección de dependencias | Construir grafos en main | Más cableado en cmd |
| Hooks de eventos/callbacks | Invertir la dirección de la dependencia | Más difícil de rastrear el flujo |
// Importación vacía - solo efecto secundario
import _ "github.com/lib/pq"// Dot import - evitar en código de aplicación
import . "example.com/app/config" // permite Port en lugar de config.Port// Detectar ciclos temprano
// go build ./... informa la cadena de rutas de importaciónimport . "foo") todavía confunden a los lectores; prefiere calificadores explícitos.foo_test pueden importar foo y sus vecinos; diseña paquetes de producción para que permanezcan acíclicos.init entre paquetes sigue el grafo de importación; los ciclos harían que eso fuera indefinido, de ahí el error de compilación.init - registry.Register("png", decode) llamado desde main para mayor claridad.cmd sin importaciones mutuas.La inicialización de paquetes se ejecuta antes de main.
Un ciclo dejaría algunos paquetes parcialmente inicializados sin un orden seguro.
Cuando un paquete existe únicamente para registrarse en un registro global en tiempo de init y tu código nunca lo llama directamente.
Sí, si expone efectos secundarios de init.
Prefiere la configuración explícita en main para el cableado de la aplicación a menos que imites patrones de drivers.
Raramente en pruebas o código generado.
El código de producción debe usar identificadores calificados según los Comentarios de Revisión de Código de Go.
Lee el error de go build - imprime la cadena de importación.
Arregla primero la preocupación compartida más baja.
Solo cuando la interfaz vive en un paquete que ninguno de los lados importa circularmente.
Ambos lados pueden depender del paquete de contratos.
Sigue siendo un mal olor de diseño incluso sin un ciclo.
Pasa las dependencias explícitamente o a través de constructores.
Los paquetes de prueba externos son unidades de compilación separadas.
Mantén los paquetes de producción acíclicos; las pruebas tienen más flexibilidad pero no deben impulsar diseños deficientes.
No - los paquetes de la biblioteca estándar no importan tu código.
Los ciclos aparecen en el grafo de tu propio módulo.
Los frameworks a veces registran rutas a través de init.
Las tablas de rutas explícitas en main o internal/server son más fáciles de auditar.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado Green Tea GC, go fix modernizers - verificar parche en la compilación), chi (última - verificar en la compilación), gin (última - verificar en la compilación), echo (última - verificar en la compilación), google.golang.org/grpc (última - verificar en la compilación), sigs.k8s.io/controller-runtime (última - verificar en la compilación), kubebuilder (última - verificar en la compilación), tinygo (última - verificar objetivos de placa en la compilación), wazero (última - verificar en la compilación), y golangci-lint (última - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 18 jul 2026