Inizializzazione dei pacchetti
Quando eseguiamo un programma Go, il compilatore Go segue un certo ordine di esecuzione per i pacchetti, i file in un pacchetto e la dichiarazione delle variabili in quei file.
Scampo del pacchetto
Uno scopo è una regione in un blocco di codice dove una variabile definita è accessibile. Uno scope di pacchetto è una regione all’interno di un pacchetto dove una variabile dichiarata è accessibile dall’interno di un pacchetto (attraverso tutti i file del pacchetto). Questa regione è il blocco più in alto di qualsiasi file nel pacchetto.
Guarda il comando go run
. Questa volta, invece di eseguire un file, abbiamo un pattern glob per includere tutti i file all’interno del pacchetto app
per l’esecuzione.
Go è abbastanza intelligente da capire un punto di ingresso dell’applicazione che è entry.go
perché ha la funzione main
. Possiamo anche usare un comando come qui sotto (l’ordine dei nomi dei file non ha importanza).
go run src/app/version.go src/app/entry.go
💡 Puoi anche usare il comando
go run app
che fa la stessa cosa e questo è un approccio migliore per eseguire un pacchetto. Il comandogo install
ogo build
richiede un nome di pacchetto, che include tutti i file all’interno di un pacchetto, quindi non dobbiamo specificarli come sopra.
Tornando al nostro problema principale, possiamo usare la variabile version
dichiarata nel file version.go
da qualsiasi punto del pacchetto anche se non è esportata (come Version
), perché è dichiarata nello scope del pacchetto.
Se la variabile version
fosse stata dichiarata all’interno di una funzione, non sarebbe stata nello scope del package e il programma di cui sopra non sarebbe riuscito a compilare.
Non è permesso ridichiarare una variabile globale con lo stesso nome nello stesso package. Quindi, una volta dichiarata la variabile version
, non può essere ridichiarata nello scope del pacchetto. Ma sei libero di ridichiararla altrove.
Inizializzazione delle variabili
Quando una variabile a
dipende da un’altra variabile b
, b
deve essere definita prima, altrimenti il programma non verrà compilato. Go segue questa regola all’interno delle funzioni.
Ma quando queste variabili sono definite nel package scope, sono dichiarate nei cicli di inizializzazione. Diamo un’occhiata al semplice esempio qui sotto.
Nell’esempio precedente, prima c
è dichiarato perché il suo valore è già dichiarato. Nel ciclo di inizializzazione successivo, b
è dichiarato, perché dipende da c
e il valore di c
è già dichiarato. Nel ciclo di inizializzazione finale, a
è dichiarato e assegnato al valore di b
. Go può gestire cicli di inizializzazione complessi come qui sotto.
Nell’esempio sopra, prima c
sarà dichiarato e poi b
come il suo valore dipende da c
e infine a
come il suo valore dipende da b
. Dovresti evitare qualsiasi ciclo di inizializzazione come sotto, dove l’inizializzazione entra in un ciclo ricorsivo.
Un altro esempio di scopo del pacchetto sarebbe, avere una funzione f
in un file separato che fa riferimento alla variabile c
dal file principale.
Funzione Init
Come la funzione main
, init
viene chiamata da Go quando un pacchetto viene inizializzato. Non prende nessun argomento e non restituisce nessun valore.
La funzione init
è dichiarata implicitamente da Go, quindi non si può fare riferimento ad essa da nessuna parte (o chiamarla come init()
). Puoi avere più funzioni init
in un file o in un pacchetto. L’ordine di esecuzione delle funzioni init
in un file sarà secondo l’ordine della loro apparizione.
È possibile avere funzioni init
in qualsiasi punto del pacchetto. Queste funzioni init
sono chiamate in ordine lessicale di nome del file (ordine alfabetico).
Dopo che tutte le funzioni init
sono state eseguite, viene chiamata la funzione main
. Quindi, il compito principale della funzione init
è di inizializzare le variabili globali che non possono essere inizializzate nel contesto globale. Per esempio, l’inizializzazione di un array.
Siccome, la sintassi for
non è valida nello scopo del pacchetto, possiamo inizializzare l’array integers
di dimensione 10
usando for
loop all’interno della funzione init
.
Alias di pacchetto
Quando importate un pacchetto, andate a creare una variabile usando la dichiarazione del pacchetto. Se stai importando più pacchetti con lo stesso nome, questo porterà ad un conflitto.
// greet/parent.go
package greetvar Message = "Hey there. I am parent."// greet/greet/child.go
package greetvar Message = "Hey there. I am child."
Quindi, usiamo gli alias di pacchetto. Indichiamo un nome di variabile tra la parola chiave import
e il nome del pacchetto che diventa la nuova variabile di riferimento al pacchetto.
Nell’esempio precedente, il pacchetto greet/greet
ora è referenziato dalla variabile child
. Se ci fai caso, abbiamo aggiunto un underscore al pacchetto greet
.
Underscore è un carattere speciale in Go che funge da contenitore null
. Poiché stiamo importando il pacchetto greet
ma non lo usiamo, il compilatore Go si lamenterà di questo. Per evitarlo, stiamo memorizzando il riferimento a quel pacchetto in _
e il compilatore Go semplicemente lo ignorerà.
L’eliminazione di un pacchetto con un underscore che sembra non fare nulla è abbastanza utile a volte quando si vuole inizializzare un pacchetto ma non usarlo.
// 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()")
}
La cosa principale da ricordare è che un pacchetto importato viene inizializzato solo una volta per pacchetto. Quindi se si hanno più dichiarazioni di importazione in un pacchetto, un pacchetto importato verrà inizializzato solo una volta nel corso dell’esecuzione del pacchetto principale.