Tudo o que você precisa saber sobre Pacotes em Go

Inicialização de pacotes

Quando executamos um programa Go, o compilador Go segue uma certa ordem de execução para pacotes, arquivos em um pacote e declaração de variáveis nesses arquivos.

Escopo do pacote

Um escopo é uma região em um bloco de código onde uma variável definida é acessível. O escopo de um pacote é uma região dentro de um pacote onde uma variável declarada é acessível de dentro de um pacote (através de todos os arquivos do pacote). Esta região é o bloco mais alto de qualquer arquivo no pacote.

Dê uma olhada no comando go run. Desta vez, ao invés de executar um arquivo, temos um padrão glob para incluir todos os arquivos dentro de app pacote para execução.

Go é inteligente o suficiente para descobrir um ponto de entrada da aplicação que é entry.go porque ele tem main função. Também podemos usar um comando como o abaixo (a ordem do nome do arquivo não importa).

go run src/app/version.go src/app/entry.go

💡 Você também pode usar o comando go run app que faz a mesma coisa e esta é uma melhor abordagem para executar um pacote. go install ou go build comando requer um nome de pacote, que inclui todos os arquivos dentro de um pacote, então não precisamos especificá-los como acima.

Voltando ao nosso problema principal, podemos usar a variável version declarada em version.go arquivo de qualquer parte do pacote mesmo que não seja exportado (como Version), porque é declarado no escopo do pacote.

Se a variável version tivesse sido declarada dentro de uma função, ela não estaria no escopo do pacote e o programa acima teria falhado na compilação.

Você não tem permissão para redeclarar uma variável global com o mesmo nome no mesmo pacote. Portanto, uma vez que version variável é declarada, ela não pode ser redeclarada no escopo do pacote. Mas você está livre para redeclarar em outro lugar.

Inicialização de variável

Quando uma variável a depende de outra variável b, b deve ser definida de antemão, caso contrário o programa não compilará. Go segue esta regra dentro das funções.

Mas quando estas variáveis são definidas no escopo do pacote, elas são declaradas em ciclos de inicialização. Vamos dar uma olhada no exemplo simples abaixo.

No exemplo acima, primeiro c é declarado porque o seu valor já está declarado. No ciclo de inicialização posterior, b é declarado, porque depende de c e o valor de c já está declarado. No ciclo de inicialização final, a é declarado e atribuído ao valor de b. Go pode lidar com ciclos complexos de inicialização como abaixo.

No exemplo acima, primeiro c será declarado e depois b pois seu valor depende de c e finalmente a pois seu valor depende de b. Você deve evitar qualquer loop de inicialização como abaixo onde a inicialização entra em um loop recursivo.

Outro exemplo de escopo de pacote seria, tendo uma função f em um arquivo separado que referencia a variável c do arquivo principal.

Função de entrada

Função de entrada main, init a função é chamada por Go quando um pacote é inicializado. Ele não leva nenhum argumento e não retorna nenhum valor.

A função init é implicitamente declarada por Go, portanto você não pode referenciá-la de qualquer lugar (ou chamá-la como init()). Você pode ter múltiplas funções init em um arquivo ou em um pacote. A ordem de execução de init função em um arquivo será de acordo com a ordem de suas aparências.

Você pode ter init função em qualquer parte do pacote. Estas init funções são chamadas em ordem alfabética (ordem alfabética).

Depois de todas as init funções serem executadas, main função é chamada. Assim, o trabalho principal da função init é inicializar variáveis globais que não podem ser inicializadas no contexto global. Por exemplo, inicialização de um array.

Desde, for a sintaxe não é válida no escopo do pacote, podemos inicializar o array integers de tamanho 10 usando for loop inside init function.

Package alias

Quando você importa um pacote, vá criar uma variável usando a declaração de pacote do pacote. Se você estiver importando vários pacotes com o mesmo nome, isso levará a um conflito.

// greet/parent.go
package greetvar Message = "Hey there. I am parent."// greet/greet/child.go
package greetvar Message = "Hey there. I am child."

Hence, nós usamos o apelido pacote. Dizemos um nome de variável entre import palavra-chave e nome do pacote que se torna a nova variável de referência do pacote.

No exemplo acima, greet/greet pacote agora é referenciado por child variável. Se você notar, nós apelidamos de greet pacote com um underscore.

Underscore é um caractere especial em Go que age como null recipiente. Como estamos importando greet pacote mas não o estamos usando, o compilador Go irá reclamar sobre ele. Para evitar isso, estamos armazenando referências desse pacote em _ e o Go compiler irá simplesmente ignorá-lo.

Aliar um pacote com um underscore que parece não fazer nada é bastante útil às vezes quando você quer inicializar um pacote mas não usá-lo.

// greet/parent.go
package greetimport "fmt"var Message = "Hey there. I am parent."func init() {
fmt.Println("greet/parent.go ==> init()")
}// greet/greet/child.go
package greetimport "fmt"var Message = "Hey there. I am child."func init() {
fmt.Println("greet/greet/child.go ==> init()")
}

O principal a lembrar é que um pacote importado é inicializado apenas uma vez por pacote. Portanto, se você tiver múltiplas declarações de importação em um pacote, um pacote importado será inicializado apenas uma vez no tempo de execução do pacote principal.