Inicializace balíčků
Při spouštění programu v jazyce Go dodržuje překladač Go určité pořadí provádění balíčků, souborů v balíčku a deklarací proměnných v těchto souborech.
Obsah balíčku
Obsah je oblast v bloku kódu, kde je přístupná definovaná proměnná. Obor balíčku je oblast v balíčku, kde je deklarovaná proměnná přístupná z balíčku (napříč všemi soubory v balíčku). Tato oblast je nejvyšším blokem libovolného souboru v balíčku.
Podívejte se na příkaz go run
. Tentokrát místo spuštění jednoho souboru máme glob vzor, který zahrnuje všechny soubory uvnitř balíčku app
ke spuštění.
Go je dost chytrý na to, aby zjistil vstupní bod aplikace, kterým je entry.go
, protože má funkci main
. Můžeme také použít příkaz jako níže (na pořadí názvů souborů nezáleží).
go run src/app/version.go src/app/entry.go
💡 Můžete také použít příkaz
go run app
, který dělá totéž a je to lepší přístup ke spuštění balíčku. Příkazgo install
nebogo build
vyžaduje název balíčku, který zahrnuje všechny soubory uvnitř balíčku, takže je nemusíme specifikovat jako výše.
Vrátíme-li se k našemu hlavnímu problému, můžeme proměnnou version
deklarovanou v souboru version.go
použít odkudkoli z balíčku, i když není exportovaná (jako Version
), protože je deklarovaná v rozsahu balíčku.
Pokud by byla proměnná version
deklarována uvnitř funkce, nebyla by v oboru balíčku a výše uvedený program by se nepodařilo zkompilovat.
Globální proměnnou se stejným jménem nesmíte ve stejném balíčku deklarovat znovu. Proto jakmile je jednou version
proměnná deklarována, nelze ji v oboru balíčku deklarovat znovu. Můžete ji však znovu deklarovat jinde.
Inicializace proměnných
Když proměnná a
závisí na jiné proměnné b
, měla by být b
definována předem, jinak se program nezkompiluje. Go se tímto pravidlem řídí uvnitř funkcí.
Pokud jsou ale tyto proměnné definovány v oboru balíčku, deklarují se v inicializačních cyklech. Podívejme se na jednoduchý příklad níže:
V uvedeném příkladu je nejprve deklarována c
, protože její hodnota je již deklarována. V pozdějším inicializačním cyklu se deklaruje b
, protože závisí na c
a hodnota c
je již deklarována. V posledním inicializačním cyklu je deklarována hodnota a
, která je přiřazena hodnotě b
. Go si poradí se složitými inicializačními cykly, jak je uvedeno níže.
V uvedeném příkladu se nejprve deklaruje c
a poté b
, protože jeho hodnota závisí na c
, a nakonec a
, protože jeho hodnota závisí na b
. Měli byste se vyhnout jakékoli inicializační smyčce jako níže, kdy se inicializace dostane do rekurzivní smyčky.
Dalším příkladem rozsahu balíčku by bylo, mít funkci f
v samostatném souboru, která odkazuje na proměnnou c
z hlavního souboru.
Init funkce
Podobně jako funkce main
, init
je funkce volána Go při inicializaci balíčku. Nepřijímá žádné argumenty a nevrací žádnou hodnotu.
Funkce init
je Go implicitně deklarována, proto se na ni nelze odnikud odkazovat (nebo ji volat jako init()
). V souboru nebo balíčku můžete mít více funkcí init
. Pořadí provádění funkce init
v souboru bude odpovídat pořadí jejich výskytu.
Funkci init
můžete mít kdekoli v balíčku. Tyto funkce init
jsou volány v lexikálním pořadí názvů souborů (abecedně).
Po provedení všech funkcí init
je volána funkce main
. Hlavním úkolem funkce init
je tedy inicializovat globální proměnné, které nelze inicializovat v globálním kontextu. Například inicializace pole.
Protože syntaxe for
není platná v oboru balíčku, můžeme inicializovat pole integers
o velikosti 10
pomocí cyklu for
uvnitř funkce init
.
Paket alias
Pokud importujete balíček, Go vytvoří proměnnou pomocí deklarace balíčku tohoto balíčku. Pokud importujete více balíčků se stejným názvem, povede to ke konfliktu.
// greet/parent.go
package greetvar Message = "Hey there. I am parent."// greet/greet/child.go
package greetvar Message = "Hey there. I am child."
Proto používáme alias balíčku. Mezi klíčové slovo import
a název balíčku uvedeme název proměnné, která se stane novou proměnnou pro odkaz na balíček.
V uvedeném příkladu se nyní na balíček greet/greet
odkazuje proměnná child
. Pokud jste si všimli, balíček greet
jsme aliasovali podtržítkem.
Podtržítko je v jazyce Go speciální znak, který funguje jako kontejner null
. Protože balík greet
importujeme, ale nepoužíváme ho, kompilátor Go si na to bude stěžovat. Abychom se tomu vyhnuli, uložíme odkaz na tento balíček do _
a překladač Go jej bude jednoduše ignorovat.
Odkazování balíčku podtržítkem, které zdánlivě nic nedělá, je někdy docela užitečné, když chceme balíček inicializovat, ale nepoužívat jej.
// 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()")
}
Hlavní věc, kterou je třeba si zapamatovat, je, že importovaný balíček se v každém balíčku inicializuje pouze jednou. Pokud tedy máte v balíčku více příkazů import, bude importovaný balíček inicializován pouze jednou za celou dobu provádění hlavního balíčku.