Todo archivo Go pertenece a un paquete, declarado en su primera línea. El código de un paquete usa otro importándolo y anteponiendo el nombre del paquete a sus nombres. Solo los nombres que empiezan por mayúscula se pueden usar desde fuera del paquete.
fmt, math y strings son paquetes de la librería estándar. Vienen con Go, así que "añadir" uno significa escribir su nombre en el bloque import. No se descarga nada. fmt.Println, math.Sqrt y strings.Repeat empiezan por mayúscula porque son exportados.
La cláusula package
La primera línea que no es un comentario de cada archivo .go es package name. Todos los archivos de un directorio tienen que usar el mismo nombre, y juntos forman un paquete. Un directorio con dos nombres de paquete distintos no compila:
found packages greeting (b.go) and greet (greet.go) in /home/ana/shop/greet
(La única excepción son los archivos de test, que pueden usar package name_test.)
package main es especial: produce un ejecutable, y el programa empieza en su func main(). Todos los demás paquetes son librerías pensadas para importarse.
Por convención, el nombre del paquete coincide con el último elemento de la ruta del directorio: el código de shop/greet es package greet. Los nombres de paquete son cortos, en minúscula y de una sola palabra, sin guiones bajos ni mayúsculas intercaladas: strconv, httptest, greet, no string_utils ni greetHelpers.
Importar paquetes
Una ruta de import es un string. Para la librería estándar es la ruta del paquete dentro del árbol de código de Go; para todo lo demás empieza con una ruta de módulo:
import (
"fmt" // standard library
"net/http" // standard library, nested path
"math/rand/v2" // standard library, version 2 of math/rand
"github.com/google/uuid" // another module (needs go get)
"example.com/shop/greet" // a package in your own module
)
La ruta es dónde vive el paquete; el nombre es lo que escribes en el código. Para net/http el nombre es http, para math/rand/v2 es rand. Escribes http.Get, nunca net/http.Get.
gofmt y goimports ordenan el bloque y, por convención, separan con una línea en blanco los imports de la librería estándar del resto.
Los imports sin usar son un error de compilación:
./main.go:5:2: "os" imported and not used
Los editores con soporte para Go ejecutan goimports al guardar, que añade los imports estándar que faltan y quita los que no se usan.
Nombres exportados y no exportados
La visibilidad en Go la decide una sola regla: si un nombre empieza por mayúscula, es exportado. Se aplica a funciones, tipos, variables, constantes, campos de structs y métodos. No hay palabras clave public, private ni protected.
| Nombre | ¿Visible fuera del paquete? |
|---|---|
func Hello() | sí |
func hello() | no |
type User struct | sí |
campo User.Name | sí |
campo User.email | no |
const MaxSize | sí |
var defaultTimeout | no |
Intentar usar un nombre no exportado desde otro paquete da un error undefined:
./main.go:11:20: undefined: greet.prefix
La regla importa incluso dentro de un solo archivo, porque otros paquetes inspeccionan tus tipos. encoding/json solo puede ver los campos exportados:
token queda fuera del JSON, en silencio. Es una ventaja cuando el campo es un dato privado y una sorpresa cuando olvidaste poner en mayúscula un campo que querías.
Repartir un paquete en varios archivos
Un paquete puede ocupar tantos archivos como quieras. Cada archivo del directorio con la misma línea package ve todos los nombres declarados en los demás, exportados o no, sin importar nada:
shop/
├── go.mod (module example.com/shop)
├── main.go
└── math.go
// main.go
package main
import "fmt"
func main() {
fmt.Println(total(2, 3))
}
// math.go
package main
func total(a, b int) int { return a + b }
main.go llama directamente a total. No hay #include ni import entre archivos del mismo paquete.
El error "undefined" de go run main.go
Es el problema de paquetes más buscado de Go, y tiene una sola causa. Ejecutar solo el archivo que contiene main compila solo ese archivo:
go run main.go
# command-line-arguments
./main.go:6:14: undefined: total
command-line-arguments es el nombre que Go da a un paquete construido a partir de una lista de archivos, y math.go no está en esa lista. Ejecuta el paquete entero:
go run .
5
Lo mismo vale para go build main.go. Usa go run . y go build (o go build ./cmd/app) y el problema no aparece nunca. Si persiste con go run ., comprueba que los dos archivos dicen package main y que ninguno tiene una restricción de compilación (//go:build) ni un sufijo _test.go que lo excluya.
Tus propios paquetes
Un subdirectorio de tu módulo es un paquete aparte. Impórtalo con la ruta del módulo de go.mod más el directorio:
shop/
├── go.mod (module example.com/shop)
├── main.go
└── greet/
└── greet.go
// greet/greet.go
package greet
const prefix = "Hi, "
// Hello returns a greeting for name.
func Hello(name string) string {
return prefix + name
}
// main.go
package main
import (
"fmt"
"example.com/shop/greet"
)
func main() {
fmt.Println(greet.Hello("Ana"))
}
Hi, Ana
Aquí la gente tropieza con dos cosas. El import es la ruta del módulo más el directorio, no una ruta relativa: "./greet" no funciona en modo módulo. Y prefix queda oculto para main porque está en minúscula; solo Hello forma parte de la API del paquete.
Paquetes internal
Un directorio llamado internal restringe quién puede importar lo que contiene. El código de shop/internal/store lo puede importar cualquier paquete que cuelgue de shop/, y nada de fuera. Otro módulo que lo intente obtiene:
use of internal package example.com/shop/internal/store not allowed
Usa internal/ para el código que quieres compartir entre tus propios paquetes sin prometérselo al resto del mundo. Puedes cambiar su API libremente, porque ningún código externo puede depender de ella.
Alias de import
Para dar a un import otro nombre local, escribe el nombre delante de la ruta. El motivo habitual son dos paquetes con el mismo nombre:
Los dos paquetes se llaman rand, así que al menos uno necesita un alias. Usa alias para los conflictos y para nombres poco claros; no renombres paquetes solo para acortarlos, porque quien lee conoce strconv y no tu sc.
Imports vacíos y con punto
Import vacío (_). Importa un paquete solo por sus efectos secundarios: se ejecutan sus variables de paquete y sus funciones init, pero no usas ninguno de sus nombres. Los drivers de bases de datos y los decodificadores de imágenes se registran así:
import (
"database/sql"
_ "github.com/lib/pq" // registers the "postgres" driver with database/sql
)
Sin el _, el import sería un error de import sin usar.
Import con punto (.). Mete los nombres exportados del paquete directamente en el ámbito de tu archivo, así que escribes Println en lugar de fmt.Println. Oculta de dónde vienen los nombres y se desaconseja fuera de unos pocos patrones de funciones auxiliares de test.
Ciclos de import
Go no permite que dos paquetes se importen mutuamente, ni directamente ni a través de una cadena:
package example.com/shop
imports example.com/shop/greet from main.go
imports example.com/shop/other from c.go
imports example.com/shop/greet from o.go: import cycle not allowed
El error enumera la cadena. Dos arreglos cubren casi todos los casos:
- Mueve el código que necesitan los dos paquetes a un tercer paquete de nivel más bajo que importen ambos.
- Si el paquete A solo necesita llamar a algo de B, define una interfaz pequeña en A y deja que el tipo de B la cumpla. A ya no importa B.
Un ciclo suele significar que los límites de los paquetes se han trazado alrededor de capas de código que en realidad van juntas, así que también es una señal para replantear la división.
Errores comunes
- Ejecutar un solo archivo.
go run main.goen un paquete de varios archivos: usago run .. - Imports relativos.
import "./utils"falla en modo módulo. Usa"example.com/yourmodule/utils". - Nombres en minúscula que querías exportar. Una función, un campo o un método que necesiten otros paquetes tiene que empezar por mayúscula.
- Nombre de paquete que no coincide con el directorio. Es legal, pero confunde: la ruta de import dice
utilsy el código dicehelpers.X. Mantenlos iguales. - Nombres de paquete cajón de sastre.
util,commonymiscno dicen nada de su contenido y tienden a convertirse en imanes de ciclos de import. Nombra los paquetes por lo que ofrecen:money,auth,slug.
Preguntas frecuentes
¿Por qué Go dice que una función del mismo paquete está sin definir?
Probablemente ejecutaste go run main.go. Nombrar un archivo compila solo ese archivo, así que faltan las funciones definidas en otros archivos del mismo paquete: undefined: total. Ejecuta go run ., que compila todos los archivos .go del directorio. Comprueba también que todos los archivos tienen la misma línea package.
¿Cómo añado el paquete fmt en Go?
fmt forma parte de la librería estándar y viene con Go, así que no hay nada que instalar. Añade import "fmt" después de la línea package y llama a sus funciones con el prefijo fmt., por ejemplo fmt.Println("hi"). Lo mismo vale para strings, os, time, net/http y cualquier otro paquete estándar.
¿Cómo importo mi propio paquete en Go?
Impórtalo con la ruta del módulo más el directorio. En un módulo llamado example.com/shop, un paquete de la carpeta greet se importa como import "example.com/shop/greet" y se usa como greet.Hello(). Solo los nombres que empiezan por mayúscula son visibles para quien importa.
¿Qué diferencia hay entre exportado y no exportado en Go?
Un nombre que empieza por mayúscula (Hello, Total, User.Name) es exportado y se puede usar desde otros paquetes. Un nombre en minúscula (hello, total, user.name) no es exportado y solo es visible dentro de su propio paquete. No hay palabras clave public ni private.
¿Cómo arreglo "import cycle not allowed" en Go?
Dos paquetes se importan mutuamente, directamente o a través de una cadena, y Go lo prohíbe. Mueve el código compartido a un tercer paquete que importen los dos, o define una interfaz pequeña en el paquete que necesita el comportamiento para que deje de importar al otro.