Pacotes são a unidade fundamental de organização e reutilização de código em Go. Todo arquivo fonte pertence a um pacote, e a estrutura de pacotes define o escopo de nomes, a visibilidade de identificadores e a modularidade do programa. Nesta aula, você aprenderá como declarar pacotes, controlar a exportação de símbolos, importar dependências e organizar projetos de forma eficiente.

Diferente de linguagens como Java ou Python, Go não possui classes nem herança; a modularidade é alcançada através de pacotes e interfaces. Compreender bem o sistema de pacotes é essencial para escrever código limpo, reutilizável e de fácil manutenção.

package

Todo arquivo Go começa com uma declaração package que define a qual pacote ele pertence. A declaração deve ser a primeira linha do arquivo (antes de qualquer importação). O nome do pacote geralmente é o mesmo nome do diretório onde o arquivo está localizado, seguindo a convenção de usar letras minúsculas e sem underscores (a menos que o nome seja composto, como net/http).

Exemplos de declaração de pacote:

package main

func main() {
    println("Olá, mundo!")
}

O pacote main é especial: ele define um executável independente. Pacotes com outros nomes são bibliotecas que podem ser importadas por outros pacotes.

Visibilidade (maiúscula/minúscula)

Em Go, a visibilidade de um identificador (variável, função, tipo, constante) é determinada pela primeira letra do seu nome. Se a primeira letra for maiúscula, o identificador é exportado (público), visível fora do pacote. Se for minúscula, é não exportado (privado), visível apenas dentro do mesmo pacote.

Exemplo:

package matematica

// Soma é exportada (maiúscula)
func Soma(a, b int) int {
    return a + b
}

// subtrai não é exportada (minúscula)
func subtrai(a, b int) int {
    return a - b
}

Essa regra simples evita a necessidade de palavras-chave como public ou private. O escopo de um identificador não exportado é o pacote inteiro, ou seja, todos os arquivos que declaram o mesmo pacote podem acessá-lo.

import

Para usar funcionalidades de outros pacotes, utilizamos a declaração import. Ela pode importar um ou mais pacotes, listando seus caminhos entre aspas duplas. O caminho é relativo ao módulo (definido no go.mod) ou ao GOPATH.

Exemplo de importação:

package main

import (
    "fmt"
    "matematica"
)

func main() {
    fmt.Println(matematica.Soma(3, 4))
}

Go permite importar pacotes com alias para evitar conflitos de nomes:

import (
    "fmt"
    m "matematica"
)

func main() {
    fmt.Println(m.Soma(3, 4))
}

Também é possível importar pacotes apenas para executar suas funções init usando o identificador em branco (_):

import _ "image/png" // registra o decodificador PNG

Organização

Go incentiva uma estrutura de diretórios simples: cada diretório representa um pacote, e todos os arquivos .go dentro desse diretório devem declarar o mesmo nome de pacote. Subdiretórios podem representar subpacotes, mas não há herança de visibilidade entre eles.

Exemplo de estrutura de projeto:

meuprojeto/
├── go.mod
├── main.go            (package main)
├── matematica/
│   ├── soma.go        (package matematica)
│   └── subtracao.go   (package matematica)
└── utils/
    └── strings.go     (package utils)

No main.go, importamos meuprojeto/matematica e meuprojeto/utils. A importação usa o módulo como prefixo. É boa prática manter os pacotes coesos e com responsabilidades únicas, evitando pacotes muito grandes ou com muitos arquivos.

Boas práticas: Use nomes curtos e descritivos para pacotes; evite nomes genéricos como util ou common. Prefira nomes que indiquem o domínio, como http, json, time. Também é recomendado não usar underscores nos nomes de pacotes, a menos que seja necessário (ex.: net/http é um pacote composto, mas o nome do diretório é http dentro de net).

Referências

Exercícios

  1. Crie um pacote chamado calculadora com uma função exportada Soma e uma função não exportada subtrai. Em seguida, escreva um programa main que importa o pacote e chama Soma.
  2. ✓ Resposta: Crie um diretório calculadora com um arquivo calculadora.go:
    package calculadora
    
    func Soma(a, b int) int {
        return a + b
    }
    
    func subtrai(a, b int) int {
        return a - b
    }
    No main.go no diretório raiz:
    package main
    
    import (
        "fmt"
        "meuprojeto/calculadora"
    )
    
    func main() {
        fmt.Println(calculadora.Soma(2, 3))
    }
  3. Explique por que a função subtrai não pode ser chamada de fora do pacote calculadora.
  4. ✓ Resposta: Porque o nome subtrai começa com letra minúscula, o que a torna não exportada (privada) ao pacote. Apenas identificadores com primeira letra maiúscula são exportados e visíveis fora do pacote.
  5. Escreva um programa que importe o pacote fmt usando um alias para f e imprima "Olá" usando f.Println.
  6. ✓ Resposta:
    package main
    
    import f "fmt"
    
    func main() {
        f.Println("Olá")
    }
  7. Qual a diferença entre importar um pacote com o identificador em branco (_) e importá-lo normalmente?
  8. ✓ Resposta: Importar com _ faz com que o pacote seja compilado e tenha suas funções init executadas, mas os identificadores do pacote não ficam acessíveis no código. É usado para efeitos colaterais, como registrar drivers de banco de dados. Já a importação normal torna os identificadores exportados disponíveis.
  9. Dada a estrutura de diretórios: projeto/main.go e projeto/meupacote/arquivo.go, qual deve ser a declaração de pacote em arquivo.go e como importá-lo em main.go?
  10. ✓ Resposta: Em arquivo.go: package meupacote. Em main.go, supondo que o módulo se chame projeto, a importação será: import "projeto/meupacote".