Todo arquivo Go pertence a um pacote, declarado na primeira linha. O código de um pacote usa outro importando-o e prefixando os nomes com o nome do pacote. Só nomes que começam com letra maiúscula podem ser usados de fora do pacote.
fmt, math e strings são pacotes da biblioteca padrão. Eles vêm com o Go, então "adicionar" um significa escrever o nome dele no bloco import. Nada é baixado. fmt.Println, math.Sqrt e strings.Repeat começam com maiúscula porque são exportados.
A cláusula package
A primeira linha que não é comentário em todo arquivo .go é package nome. Todos os arquivos de um diretório precisam usar o mesmo nome, e juntos formam um pacote. Um diretório com dois nomes de pacote diferentes não compila:
found packages greeting (b.go) and greet (greet.go) in /home/ana/shop/greet
(A única exceção são os arquivos de teste, que podem usar package nome_test.)
package main é especial: ele gera um executável, e o programa começa na sua func main(). Todo outro pacote é uma biblioteca feita para ser importada.
Por convenção, o nome do pacote é igual ao último elemento do caminho do diretório: o código em shop/greet é package greet. Nomes de pacote são curtos, em minúsculas, de uma palavra só, sem underscores nem maiúsculas no meio: strconv, httptest, greet, e não string_utils ou greetHelpers.
Importando pacotes
Um caminho de import é uma string. Para a biblioteca padrão, é o caminho do pacote dentro da árvore de código do Go; para todo o resto, começa com um caminho 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
)
O caminho é onde o pacote fica; o nome é o que você digita no código. Para net/http o nome é http, para math/rand/v2 é rand. Você escreve http.Get, nunca net/http.Get.
O gofmt e o goimports ordenam o bloco e, por convenção, separam os imports da biblioteca padrão dos demais com uma linha em branco.
Imports sem uso são erro de compilação:
./main.go:5:2: "os" imported and not used
Editores com suporte a Go executam o goimports ao salvar, o que acrescenta os imports padrão que faltam e remove os que não são usados.
Nomes exportados e não exportados
A visibilidade em Go é decidida por uma regra: se um nome começa com letra maiúscula, ele é exportado. Isso vale para funções, tipos, variáveis, constantes, campos de struct e métodos. Não existem as palavras-chave public, private ou protected.
| Nome | Visível fora do pacote? |
|---|---|
func Hello() | sim |
func hello() | não |
type User struct | sim |
campo User.Name | sim |
campo User.email | não |
const MaxSize | sim |
var defaultTimeout | não |
Tentar usar um nome não exportado a partir de outro pacote dá um erro undefined:
./main.go:11:20: undefined: greet.prefix
A regra importa mesmo dentro de um único arquivo, porque outros pacotes inspecionam os seus tipos. O encoding/json só enxerga campos exportados:
token fica de fora do JSON, em silêncio. Isso é um recurso quando o campo é um dado privado, e uma surpresa quando você esqueceu de pôr em maiúscula um campo que queria.
Dividindo um pacote em vários arquivos
Um pacote pode ter quantos arquivos você quiser. Todo arquivo do diretório com a mesma linha package enxerga todos os nomes declarados nos outros, exportados ou não, sem 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 chama total diretamente. Não existe #include nem import entre arquivos do mesmo pacote.
O erro "undefined" do go run main.go
Esse é o problema de pacotes mais buscado em Go, e ele tem uma causa só. Executar apenas o arquivo que contém main compila apenas esse arquivo:
go run main.go
# command-line-arguments
./main.go:6:14: undefined: total
command-line-arguments é o nome que o Go dá a um pacote montado a partir de uma lista de arquivos, e math.go não está nessa lista. Execute o pacote inteiro:
go run .
5
O mesmo vale para go build main.go. Use go run . e go build (ou go build ./cmd/app) e o problema nunca aparece. Se ele continuar com go run ., confira se os dois arquivos dizem package main e se nenhum deles tem uma restrição de build (//go:build) ou o sufixo _test.go que o exclua.
Os seus próprios pacotes
Um subdiretório do seu módulo é um pacote separado. Importe-o com o caminho do módulo do go.mod mais o diretório:
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
Duas coisas pegam as pessoas aqui. O import é o caminho do módulo mais o diretório, não um caminho relativo: "./greet" não funciona no modo de módulos. E prefix fica escondido do main porque está em minúscula; só Hello faz parte da API do pacote.
Pacotes internal
Um diretório chamado internal restringe quem pode importar o que está dentro dele. O código em shop/internal/store pode ser importado por qualquer pacote com raiz em shop/, e por nada fora disso. Outro módulo que tentar recebe:
use of internal package example.com/shop/internal/store not allowed
Use internal/ para código que você quer compartilhar entre os seus próprios pacotes sem prometê-lo ao resto do mundo. Você pode mudar a API dele à vontade, já que nenhum código de fora pode depender dele.
Aliases de import
Dê a um import um nome local diferente escrevendo o nome antes do caminho. O motivo comum são dois pacotes com o mesmo nome:
Os dois pacotes se chamam rand, então pelo menos um precisa de alias. Use aliases para conflitos e para nomes pouco claros; não renomeie pacotes só para encurtá-los, já que quem lê conhece strconv, e não o seu sc.
Imports em branco e com ponto
Import em branco (_). Importa um pacote só pelos efeitos colaterais: as variáveis de pacote e as funções init dele executam, mas você não usa nenhum dos nomes dele. Drivers de banco de dados e decodificadores de imagem se registram desse jeito:
import (
"database/sql"
_ "github.com/lib/pq" // registers the "postgres" driver with database/sql
)
Sem o _, o import daria erro de import sem uso.
Import com ponto (.). Coloca os nomes exportados do pacote direto no escopo do seu arquivo, então você escreve Println em vez de fmt.Println. Isso esconde de onde vêm os nomes e é desencorajado fora de alguns padrões de funções auxiliares de teste.
Ciclos de import
O Go não permite que dois pacotes importem um ao outro, diretamente ou por uma cadeia:
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
O erro lista a cadeia. Duas correções cobrem quase todos os casos:
- Mova o código de que os dois pacotes precisam para um terceiro pacote, de nível mais baixo, que os dois importam.
- Se o pacote A só precisa chamar algo em B, defina uma pequena interface em A e deixe o tipo de B satisfazê-la. A deixa de importar B.
Um ciclo normalmente significa que as fronteiras dos pacotes foram traçadas em volta de camadas de código que na verdade andam juntas, então ele também é um sinal para repensar a divisão.
Erros comuns
- Executar um único arquivo.
go run main.goem um pacote com vários arquivos: usego run .. - Imports relativos.
import "./utils"falha no modo de módulos. Use"example.com/yourmodule/utils". - Nomes em minúscula que você queria exportar. Uma função, campo ou método de que outros pacotes precisam tem que começar com letra maiúscula.
- Nome de pacote que não bate com o diretório. É válido, mas confunde: o caminho do import diz
utilse o código dizhelpers.X. Mantenha os dois iguais. - Nomes de pacote genéricos.
util,commonemiscnão dizem nada sobre o conteúdo e tendem a virar ímãs de ciclos de import. Dê aos pacotes o nome do que eles oferecem:money,auth,slug.
Perguntas frequentes
Por que o Go diz que uma função do mesmo pacote está undefined?
Você provavelmente executou go run main.go. Apontar um arquivo compila só esse arquivo, então funções definidas em outros arquivos do mesmo pacote ficam faltando: undefined: total. Execute go run ., que compila todos os arquivos .go do diretório. Confira também se todos os arquivos têm a mesma linha package.
Como adicionar o pacote fmt em Go?
O fmt faz parte da biblioteca padrão e vem com o Go, então não há nada para instalar. Acrescente import "fmt" depois da linha package e chame as funções dele com o prefixo fmt., por exemplo fmt.Println("hi"). O mesmo vale para strings, os, time, net/http e qualquer outro pacote padrão.
Como importar o meu próprio pacote em Go?
Importe-o pelo caminho do módulo mais o diretório. Em um módulo chamado example.com/shop, um pacote na pasta greet é importado como import "example.com/shop/greet" e usado como greet.Hello(). Só nomes que começam com letra maiúscula ficam visíveis para quem importa.
Qual a diferença entre exportado e não exportado em Go?
Um nome que começa com letra maiúscula (Hello, Total, User.Name) é exportado e pode ser usado a partir de outros pacotes. Um nome com minúscula (hello, total, user.name) não é exportado e só é visível dentro do próprio pacote. Não existem as palavras-chave public ou private.
Como corrigir "import cycle not allowed" em Go?
Dois pacotes importam um ao outro, diretamente ou por uma cadeia, o que o Go proíbe. Mova o código compartilhado para um terceiro pacote que os dois importam, ou defina uma pequena interface no pacote que precisa do comportamento, para que ele não importe mais o outro.