Alles wat u moet weten over Packages in Go

Package initialisatie

Wanneer we een Go programma uitvoeren, volgt de Go compiler een bepaalde uitvoeringsvolgorde voor packages, bestanden in een package en variabele declaratie in die bestanden.

Package scope

Een scope is een regio in een codeblok waar een gedefinieerde variabele toegankelijk is. Een package scope is een regio binnen een package waar een gedeclareerde variabele toegankelijk is vanuit een package (over alle bestanden in de package). Dit gebied is het bovenste blok van elk bestand in het pakket.

Kijk eens naar het commando go run. Deze keer, in plaats van het uitvoeren van één bestand, hebben we een glob patroon om alle bestanden binnen app package op te nemen voor uitvoering.

Go is slim genoeg om een entry point van de applicatie te achterhalen en dat is entry.go omdat het main functie heeft. We kunnen ook een commando zoals hieronder gebruiken (de volgorde van de bestandsnaam doet er niet toe).

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

💡 Je kunt ook go run app commando gebruiken dat hetzelfde doet en dit is een betere aanpak om een pakket uit te voeren. go install of go build vereist een package-naam, die alle bestanden in een package bevat, dus we hoeven ze niet te specificeren zoals hierboven.

Terugkomend op ons hoofdprobleem, we kunnen variabele version gedeclareerd in version.go bestand overal in het package gebruiken, ook al wordt het niet geëxporteerd (zoals Version), omdat het wordt gedeclareerd in package scope.

Als de variabele version binnen een functie zou zijn gedeclareerd, zou deze niet in de package scope zijn geweest en zou het bovenstaande programma niet hebben kunnen compileren.

U mag een globale variabele met dezelfde naam niet opnieuw declareren in hetzelfde package. Dus, zodra version variabele is gedeclareerd, kan het niet opnieuw worden gedeclareerd in de package scope. Maar u bent vrij om elders opnieuw te declareren.

Variabele initialisatie

Wanneer een variabele a afhankelijk is van een andere variabele b, moet b van te voren worden gedefinieerd, anders zal het programma niet compileren. Go volgt deze regel binnen functies.

Maar wanneer deze variabelen worden gedefinieerd in package scope, worden ze gedeclareerd in initialisatiecycli. Laten we eens kijken naar het eenvoudige voorbeeld hieronder.

In het bovenstaande voorbeeld wordt eerst c gedeclareerd omdat de waarde ervan al is gedeclareerd. In de volgende initialisatiecyclus wordt b gedeclareerd, omdat deze afhankelijk is van c en de waarde van c al is gedeclareerd. In de laatste initialisatiecyclus wordt a gedeclareerd en toegewezen aan de waarde van b. Go kan omgaan met complexe initialisatiecycli zoals hieronder.

In het bovenstaande voorbeeld wordt eerst c gedeclareerd en vervolgens b omdat de waarde daarvan afhangt van c en ten slotte a omdat de waarde daarvan afhangt van b. U moet een initialisatielus zoals hieronder vermijden, waarbij de initialisatie in een recursieve lus terechtkomt.

Een ander voorbeeld van package scope zou zijn, dat u een functie f in een apart bestand hebt, die verwijst naar variabele c uit het hoofdbestand.

Init functie

Zoals main functie, init wordt de functie door Go aangeroepen wanneer een pakket wordt geïnitialiseerd. De functie init wordt impliciet door Go gedeclareerd, zodat u er nergens naar kunt verwijzen (of de functie kunt aanroepen zoals init()). U kunt meerdere init functies in een bestand of een package hebben. De volgorde waarin init-functies in een bestand worden uitgevoerd, wordt bepaald door de volgorde waarin ze verschijnen.

U kunt init-functies overal in het pakket hebben. Deze init-functies worden aangeroepen in lexicale bestandsnaamvolgorde (alfabetische volgorde).

Nadat alleinit-functies zijn uitgevoerd, wordt main-functie aangeroepen. De belangrijkste taak van de functie init is dus om globale variabelen te initialiseren die niet in de globale context kunnen worden geïnitialiseerd. Bijvoorbeeld de initialisatie van een array.

Omdat de syntax for niet geldig is in package scope, kunnen we de array integers van grootte 10 initialiseren met behulp van for loop binnen init functie.

Pakket alias

Wanneer u een pakket importeert, gaat u een variabele maken met behulp van de pakketdeclaratie van het pakket. Als u meerdere pakketten met dezelfde naam importeert, leidt dit tot een conflict.

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

Hierom gebruiken we package alias. Tussen het trefwoord import en de pakketnaam plaatsen we een variabelenaam die de nieuwe variabele wordt om naar het pakket te verwijzen.

In het bovenstaande voorbeeld wordt naar het pakket greet/greet nu verwezen door variabele child. Zoals u ziet, hebben we greet package gealiased met een underscore.

Underscore is een speciaal teken in Go dat fungeert als null container. Omdat we greet package importeren maar niet gebruiken, zal de Go compiler hierover klagen. Om dat te voorkomen, slaan we de referentie van dat pakket op in _ en Go compiler zal het gewoon negeren.

Het linken van een pakket met een underscore die niets lijkt te doen, is soms erg handig wanneer je een pakket wilt initialiseren maar niet gebruiken.

// 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()")
}

Het belangrijkste om te onthouden is dat een geïmporteerd pakket slechts eenmaal per pakket wordt geïnitialiseerd. Als je dus meerdere import-statements in een pakket hebt, wordt een geïmporteerd pakket maar één keer geïnitialiseerd tijdens de uitvoering van het hoofdpakket.