Inițializarea pachetelor
Când rulăm un program Go, compilatorul Go urmează o anumită ordine de execuție a pachetelor, a fișierelor dintr-un pachet și a declarațiilor variabilelor din acele fișiere.
Scopul pachetului
Un domeniu este o regiune dintr-un bloc de cod în care o variabilă definită este accesibilă. Domeniul de cuprindere al unui pachet este o regiune dintr-un pachet în care o variabilă declarată este accesibilă din interiorul unui pachet (în toate fișierele din pachet). Această regiune este cel mai de sus bloc din orice fișier din pachet.
Aruncă o privire la comanda go run
. De data aceasta, în loc să executăm un singur fișier, avem un model glob pentru a include toate fișierele din interiorul pachetului app
pentru a fi executate.
Go este suficient de inteligent pentru a-și da seama de un punct de intrare al aplicației, care este entry.go
, deoarece are funcția main
. De asemenea, putem utiliza o comandă ca cea de mai jos (ordinea numelor de fișiere nu contează).
go run src/app/version.go src/app/entry.go
💡 Puteți utiliza și comanda
go run app
care face același lucru și aceasta este o abordare mai bună pentru a executa un pachet. Comandago install
saugo build
necesită un nume de pachet, care include toate fișierele din interiorul unui pachet, astfel încât nu trebuie să le specificăm ca mai sus.
Întorcându-ne la problema noastră principală, putem utiliza variabila version
declarată în fișierul version.go
de oriunde din pachet, chiar dacă nu este exportată (ca Version
), deoarece este declarată în domeniul de aplicare al pachetului.
Dacă variabila version
ar fi fost declarată în interiorul unei funcții, nu s-ar fi aflat în domeniul pachetului și programul de mai sus nu ar fi reușit să se compileze.
Nu aveți voie să declarați din nou o variabilă globală cu același nume în același pachet. Prin urmare, odată ce version
variabila este declarată, ea nu poate fi redeclarată în domeniul de aplicare al pachetului. Dar sunteți liber să o redeclarați în altă parte.
Inițializarea variabilelor
Când o variabilă a
depinde de o altă variabilă b
, b
trebuie definită în prealabil, altfel programul nu se va compila. Go respectă această regulă în interiorul funcțiilor.
În exemplul de mai sus, prima c
este declarată pentru că valoarea sa este deja declarată. În ciclul de inițializare ulterior, b
este declarat, deoarece depinde de c
și valoarea lui c
este deja declarată. În ciclul final de inițializare, a
este declarat și atribuit la valoarea lui b
. Go poate gestiona cicluri de inițializare complexe, cum ar fi cele de mai jos.
În exemplul de mai sus, mai întâi va fi declarat c
, apoi b
, deoarece valoarea sa depinde de c
și, în final, a
, deoarece valoarea sa depinde de b
. Ar trebui să evitați orice buclă de inițializare ca cea de mai jos, unde inițializarea intră într-o buclă recursivă.
Un alt exemplu de domeniu de aplicare a pachetului ar fi, având o funcție f
într-un fișier separat care face referire la variabila c
din fișierul principal.
Funcția Init
Ca funcția main
, init
funcția init
este apelată de Go atunci când un pachet este inițializat. Ea nu acceptă niciun argument și nu returnează nicio valoare.
Funcțiainit
este declarată implicit de Go, prin urmare nu o puteți referi de nicăieri (sau apela cainit()
). Puteți avea mai multe funcțiiinit
într-un fișier sau într-un pachet. Ordinea de execuție a funcțieiinit
într-un fișier va fi în funcție de ordinea apariției lor.
Puteți avea funcția init
oriunde în pachet. Aceste funcții init
sunt apelate în ordinea lexicală a numelui de fișier (ordine alfabetică).
După ce toate funcțiileinit
sunt executate, este apelată funcția main
. Prin urmare, principala sarcină a funcției init
este de a inițializa variabilele globale care nu pot fi inițializate în contextul global. De exemplu, inițializarea unui array.
Din moment ce sintaxa for
nu este valabilă în cadrul pachetului, putem inițializa array-ul integers
de dimensiune 10
folosind bucla for
în interiorul funcției init
.
Aliasul pachetului
Când importați un pachet, Go creează o variabilă folosind declarația pachetului respectiv. Dacă importați mai multe pachete cu același nume, acest lucru va duce la un conflict.
// greet/parent.go
package greetvar Message = "Hey there. I am parent."// greet/greet/child.go
package greetvar Message = "Hey there. I am child."
În consecință, folosim alias de pachet. Precizăm un nume de variabilă între cuvântul cheie import
și numele pachetului, care devine noua variabilă pentru a face referire la pachet.
În exemplul de mai sus, pachetul greet/greet
este acum referit prin variabila child
. Dacă observați, am aliasat pachetul greet
cu un semn de subliniere.
Semnul de subliniere este un caracter special în Go care acționează ca un container null
. Deoarece importăm pachetul greet
, dar nu îl folosim, compilatorul Go se va plânge de acest lucru. Pentru a evita acest lucru, stocăm referința acelui pachet în _
, iar compilatorul Go o va ignora pur și simplu.
Aliind un pachet cu un underscore care pare să nu facă nimic este destul de util uneori, atunci când doriți să inițializați un pachet, dar nu îl utilizați.
// 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()")
}
Principalul lucru de reținut este că, un pachet importat este inițializat o singură dată pentru fiecare pachet. Prin urmare, dacă aveți mai multe declarații de import într-un pachet, un pachet importat va fi inițializat o singură dată pe durata de viață a execuției pachetului principal.
.