Allt du behöver veta om paket i Go

Paketinitialisering

När vi kör ett Go-program följer Go-kompilatorn en viss exekveringsordning för paket, filer i ett paket och variabeldeklarationer i dessa filer.

Paketomfång

Ett omfång är ett område i ett kodblock där en definierad variabel är tillgänglig. Ett paketomfång är ett område inom ett paket där en deklarerad variabel är tillgänglig från ett paket (över alla filer i paketet). Detta område är det översta blocket i alla filer i paketet.

Ta en titt på kommandot go run. Den här gången har vi istället för att exekvera en fil ett globmönster för att inkludera alla filer i app-paketet för exekvering.

Go är smart nog att räkna ut en ingångspunkt för programmet som är entry.go eftersom den har main-funktionen. Vi kan också använda ett kommando som nedan (filnamnsordningen spelar ingen roll).

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

💡 Du kan också använda kommandot go run app som gör samma sak och detta är en bättre metod för att exekvera ett paket. Kommando go install eller go build kräver ett paketnamn, vilket inkluderar alla filer i ett paket, så vi behöver inte specificera dem som ovan.

Vid återgång till vår huvudfråga kan vi använda variabeln version som deklarerats i filen version.go var som helst i paketet, även om den inte exporteras (som Version), eftersom den deklareras i paketets omfattning.

Om version-variabeln hade deklarerats i en funktion hade den inte funnits i paketomfånget och ovanstående program hade inte kunnat kompileras.

Det är inte tillåtet att omdeklarera en global variabel med samma namn i samma paket. När version variabeln väl har deklarerats kan den därför inte omdeklareras i paketets omfattning. Men det står dig fritt att återdeklarera någon annanstans.

Variabelns initialisering

När en variabel a är beroende av en annan variabel b ska b definieras i förväg, annars kan programmet inte kompileras. Go följer denna regel inuti funktioner.

Men när dessa variabler definieras i paketomfånget deklareras de i initialiseringscykler. Låt oss ta en titt på det enkla exemplet nedan:

I exemplet ovan deklareras först c eftersom dess värde redan är deklarerat. I den senare initialiseringscykeln deklareras b eftersom den är beroende av c och värdet av c redan är deklarerat. I den sista initialiseringscykeln deklareras a och tilldelas värdet för b. Go kan hantera komplexa initialiseringscykler som nedan:

I exemplet ovan kommer först c att deklareras och sedan b eftersom dess värde är beroende av c och till sist a eftersom dess värde är beroende av b. Du bör undvika en initialiseringsslinga som nedan där initialiseringen hamnar i en rekursiv slinga.

Ett annat exempel på paketets räckvidd är att ha en funktion f i en separat fil som refererar till variabeln c från huvudfilen.

Init-funktion

Likt main funktion, init funktionen anropas av Go när ett paket initieras. Den tar inte emot några argument och returnerar inget värde.

Funktionen init deklareras implicit av Go, därför kan du inte referera till den från någonstans (eller anropa den som init()). Du kan ha flera init-funktioner i en fil eller ett paket. Ordningen för utförandet av init-funktioner i en fil kommer att vara enligt den ordning i vilken de dyker upp.

Du kan ha init-funktionen var som helst i paketet. Dessa init-funktioner anropas i lexikalisk filnamnsordning (alfabetisk ordning).

När allainit-funktioner har utförts anropas main-funktionen. Därför är init-funktionens huvuduppgift att initialisera globala variabler som inte kan initialiseras i den globala kontexten. Exempelvis initialisering av en array.

for-syntaxen inte är giltig i paketets räckvidd kan vi initialisera arrayen integers med storleken 10 med hjälp av for-slinga inuti init-funktionen.

Paketalias

När du importerar ett paket skapar Go en variabel med hjälp av paketdeklarationen för paketet. Om du importerar flera paket med samma namn leder detta till en konflikt.

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

Därför använder vi package alias. Vi anger ett variabelnamn mellan nyckelordet import och paketnamnet som blir den nya variabeln för att referera till paketet.

I exemplet ovan refereras nu paketet greet/greet av variabeln child. Om du lägger märke till det har vi gett greet paketet ett alias med ett understreck.

Understreck är ett specialtecken i Go som fungerar som null behållare. Eftersom vi importerar greet-paketet men inte använder det kommer Go-kompilatorn att klaga på det. För att undvika det lagrar vi referensen till det paketet i _ och Go-kompilatorn kommer helt enkelt att ignorera det.

Aliasing a package with a underscore which seems to do nothing is quite useful sometimes when you want to initialize a package but not use it.

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

Det viktigaste att komma ihåg är att ett importerat paket initialiseras endast en gång per paket. Om du har flera importmeddelanden i ett paket kommer därför ett importerat paket att initialiseras endast en gång under huvudpaketets livstid.