Obsah
1. Go – minimalistický a překvapivě výkonný programovací jazyk
2. Go není „pouze“ vylepšený programovací jazyk C
3. Popularita programovacího jazyka Go mezi vývojáři
4. Instalace základních nástrojů pro jazyk Go do Fedory
5. Nastavení proměnné prostředí GOPATH
6. Základní kontrola instalace
7. První demonstrační příklad – klasický „Hello world!“ ve dvou variantách
9. Doporučovaný styl zápisu programů
10. Nástroj gofmt určený pro přeformátování zdrojových kódů
11. Kontrola použití importovaných modulů
12. Plná podpora Unicode v programech
13. Základy typového systému – odvození typu proměnné z hodnoty
14. Kontrola typu hodnoty přiřazované do proměnné
16. Specifikace typu argumentů funkcí
18. Vrácení většího množství hodnot z funkce
19. Repositář s demonstračními příklady
1. Go – minimalistický a překvapivě výkonný programovací jazyk
Jak jsme se již zmínili v perexu, začíná dnes na Rootu vycházet nový seriál, v němž se postupně seznámíme se zajímavým a relativně novým programovacím jazykem nazvaným Go. Jedná se o programovací jazyk umožňující překlad zdrojových kódů do nativního (strojového) kódu, takže výsledkem by měly být rychlé a paměťově efektivní aplikace (alespoň teoreticky) srovnatelné s výsledky, které jsou produkované překladači jazyků C, C++, D či Rust (popř. Fortran, FreePascal apod.). Současně ovšem Go používá automatickou správu paměti (GC – garbage collector), takzvané gorutiny a kanály a současně i poměrně minimalistickou syntaxi, čímž se od dvojice C a C++ dosti podstatným způsobem odlišuje (Rust, s nímž jsme se na Rootu již seznámili, je ovšem koncipován odlišným způsobem, i když je s jazykem Go poměrně často srovnáván, například v tomto pěkném článečku).
Tento programovací jazyk krátce po svém oficiálním uvedení v roce 2009 vzbudil poměrně velký ohlas, neboť se jedná o staticky typovaný jazyk kompilovaný do nativního kódu – mohlo by se tedy zdát, že je určen pro oblast doposud ovládanou programovacími jazyky C a C++. Jak si však řekneme v dalším textu, byla praxe poněkud složitější a Go začal být – možná poněkud překvapivě i pro jeho samotné tvůrce – používán těmi vývojáři, kteří tvořili aplikace založené na technologiích typu Node.js, Pythonu či Ruby (viz též třetí kapitolu). Využíván je též pro psaní síťových utilit atd. Mezi větší a známější aplikace naprogramované v Go patří především OpenShift, Kubernetes a zapomenout nesmíme ani na Docker.
2. Go není „pouze“ vylepšený programovací jazyk C
Víme již, že mezi autory návrhu i první implementace programovacího jazyka Go patří mj. i Ken Thompson. Když si uvědomíme, že Ken vytvořil i (dnes prehistorický) programovací jazyk B, který je předchůdcem slavného jazyka C, mohlo by se zdát, že Go bude pouhým vylepšením programovacího jazyka C. Je sice pravda, že Go alespoň částečně z jazyka C vychází, ovšem z hlediska syntaxe jsou si mnohem bližší například jazyky C a Java než C a Go. Tvůrci programovacího jazyka Go se navíc snažili poučit se z některých problematických rysů jazyků C a taktéž C++ a navrhli Go takovým způsobem, aby byla jeho syntaxe jednodušší a snáze pochopitelná (sami se po prohlédnutí zdrojových kódů můžete rozhodnout, do jaké míry se to splnilo). Samotný jazyk je velmi konzervativní a někteří kritici dokonce (s nadsázkou) tvrdí, že zcela ignoruje všechny novinky, které se na poli programovacích jazyků za posledních dvacet let objevily.
Navíc se tvůrci jazyka Go zaměřili i na další typické problémy, s nimiž se musí vypořádat prakticky všichni programátoři používající jazyky C, C++ a vlastně i uživatelé Rustu (ti nepřímo): jedná se o problematiku správy paměti a uvolňování zdrojů (resources) a v neposlední řadě i o podporu pro paralelní běh částí programů, například částí komunikujících přes síť, pracujících se souborovým systémem atd. Z tohoto důvodu byly přímo do jazyka Go přidány dvě technologie: automatický správce paměti a takzvané gorutiny (goroutines). S oběma těmito technologiemi se seznámíme v navazujících částech tohoto seriálu. Některé technologie naopak do Go přidány nebyly (slova autorů o filozofii jazyka), a to z toho důvodu, aby byl jazyk snadno použitelný a snadno implementovatelný. Právě fakt, že se v Go zpočátku neobjevily dnes již široce akceptované technologie, jako je obsluha výjimek či práce s generickými datovými typy, byl kritizován; ovšem Go se postupně mění a přidávají se do něj další vlastnosti (ovšem poměrně konzervativním způsobem, což je podle mého názoru v této oblasti jen dobře). Viz též https://golang.org/doc/faq#Why_doesnt_Go_have_feature_X.
3. Popularita programovacího jazyka Go mezi vývojáři
Programovací jazyk Go a jeho základní knihovny byly původně navrženy takovým způsobem, aby se v tomto jazyku daly efektivně psát různé síťové aplikace, které by navíc netrpěly na potenciální problémy typu „buffer overflow“ atd. Právě z toho důvodu, že síťové a serverové aplikace musí v typické konfiguraci obsluhovat velké množství paralelně běžících požadavků, byly do jazyka Go přidány již výše zmíněné gorutiny. A nutno říci, že Go se v této oblasti skutečně používá. Taktéž se předpokládalo, že se Go bude používat pro vývoj těch aplikací, které jsou nyní psány v C či C++. Tento přechod sice skutečně nastal, ovšem prozatím ne v příliš velké míře (a to se bavíme o použití v serverových a desktopových aplikacích, protože v oblasti mikrořadičů se s Go sice laboruje, ale především u relativně výkonných MCU typicky založených na čipech Cortex-M, nikoli na malých osmibitových a šestnáctibitových mikrořadičích).
Nastala ovšem zajímavější situace, o níž jsme se krátce zmínili v úvodu – programovací jazyk Go a jeho možnosti objevili programátoři, kteří psali své síťové a speciálně pak webové aplikace v Pythonu, Ruby či Java/TypeScriptu. Přepis do jazyka Go mnohdy znamenal řádový a někdy i dvouřádový (100×) nárůst výkonu těchto aplikací. Do jisté míry je nárůst výkonu způsobem překladem do nativního kódu, ovšem nesmíme zapomenout na gorutiny, které nejsou (na rozdíl od klasických vláken) příliš náročné na paměť, takže se můžeme setkat s aplikacemi, v nichž bez větších problémů běží stovky či dokonce tisíce gorutin.
4. Instalace základních nástrojů pro jazyk Go do Fedory
Popišme si nyní instalaci základních nástrojů programovacího jazyka Go do distribuce Fedora. Instalace bude na všech třech posledních vydáních Fedory prakticky totožná, takže se zaměřme na Fedoru 29, pro niž byl vytvořen balíček s Go verze 1.11 (což je v současnosti nejnovější stabilní verze tohoto jazyka). Instalaci provedeme buď přímo z terminálu s přihlášeným superuživatelem (rootem):
# dnf install golang
Alternativně samozřejmě můžeme použít instalaci s využitím nástroje sudo, pokud má ovšem přihlášený uživatel příslušná práva:
$ sudo dnf install golang
V obou případech by měla instalace proběhnout prakticky totožným způsobem:
Last metadata expiration check: 3:00:43 ago on Thu Nov 15 01:24:13 2018. Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: golang x86_64 1.11.2-1.fc29 updates 625 k Installing dependencies: golang-bin x86_64 1.11.2-1.fc29 updates 90 M golang-src noarch 1.11.2-1.fc29 updates 6.4 M ... ... ... Transaction Summary ================================================================================ Install 35 Packages Total download size: 163 M Installed size: 541 M Is this ok [y/N]:
Povšimněte si, že se po nainstalování rozbalí přibližně 514 MB dat, což by pro základní sadu nástrojů byla poměrně vysoká hodnota, ovšem Fedora 29, na níž si práci s jazykem Go ukazujeme, byla nainstalována s minimální sadou balíčků a tudíž neobsahovala ani základní vývojové nástroje (Go například vyžaduje Git atd.). Samotné Go se všemi svými nástroji vyžaduje necelých 200 MB diskového prostoru, přičemž tarball má zhruba poloviční velikost.
Po odpovědi „Y“ se instalace rozběhne a pochopitelně se nijak zásadně neliší od instalace jakéhokoli jiného balíčku:
Downloading Packages: (1/35): apr-util-bdb-1.6.1-8.fc29.x86_64.rpm 75 kB/s | 12 kB 00:00 (2/35): apr-util-1.6.1-8.fc29.x86_64.rpm 543 kB/s | 90 kB 00:00 (3/35): apr-util-openssl-1.6.1-8.fc29.x86_64.rpm 3.9 MB/s | 14 kB 00:00 ... (10/35): go-srpm-macros-2-18.fc29.noarch.rpm 442 kB/s | 11 kB 00:00 ... (24/35): golang-1.11.2-1.fc29.x86_64.rpm 401 kB/s | 625 kB 00:01 (35/35): golang-bin-1.11.2-1.fc29.x86_64.rpm 653 kB/s | 90 MB 02:21 --------------------------------------------------------------------------------------------------- Total 1.1 MB/s | 163 MB 02:29 ... ... ... Complete!
Pro jistotu se ještě přesvědčíme, že jsou k dispozici příkazy go a gofmt:
$ whereis -b go go: /usr/bin/go $ whereis -b gofmt go: /usr/bin/gofmt
5. Nastavení proměnné prostředí GOPATH
Po instalaci je vhodné nastavit proměnnou prostředí (environment variable) nazvanou GOPATH. Tato proměnná bude použita pro určení cíle překladu, pro hledání zdrojových kódů modulů atd. Dnes ji sice ještě nebudeme nutně potřebovat, ale je vhodné si tuto proměnnou připravit pro další části tohoto seriálu, kde již budeme pracovat s větším množstvím projektů a knihoven. Proměnná bude nastavována při startu BASHe, takže pokud používáte jiný shell, budete si muset upravit následující příkazy:
$ mkdir -p $HOME/go $ echo 'export GOPATH=$HOME/go' >> $HOME/.bashrc $ source $HOME/.bashrc
Nyní by již měla být proměnná nastavena, o čemž se můžeme snadno přesvědčit (shell není zapotřebí spouštět znovu, protože jsme použili příkaz source):
$ echo $GOPATH /home/tester/go
6. Základní kontrola instalace
Po instalaci základních nástrojů programovacího jazyka Go můžeme odzkoušet, zda jsou tyto nástroje skutečně použitelné. Většina funkcí je dostupná přes „univerzální“ příkaz go, takže si pro začátek vypíšeme, která verze jazyka je vlastně aktuálně dostupná. K tomu slouží tato varianta příkazu:
$ go version
V našem případě by se měla vypsat verze 1.11.2:
go version go1.11.2 linux/amd64
Při instalaci starší verze (například na Fedoru 27) se samozřejmě vypíše odlišná verze jazyka:
go version go1.9.7 linux/amd64
Vraťme se však k popisu základních nástrojů, které mají vývojáři k dispozici. Po spuštění samotného nástroje go bez dalších parametrů by se měly vypsat všechny dostupné příkazy:
$ go
Výstup zobrazený na terminálu/konzoli by měl u verze 1.11 vypadat přibližně následovně:
Go is a tool for managing Go source code. Usage: go [arguments] The commands are: bug start a bug report build compile packages and dependencies clean remove object files and cached files doc show documentation for package or symbol env print Go environment information fix update packages to use new APIs fmt gofmt (reformat) package sources generate generate Go files by processing source get download and install packages and dependencies install compile and install packages and dependencies list list packages or modules mod module maintenance run compile and run Go program test test packages tool run specified go tool version print Go version vet report likely mistakes in packages Use "go help <command>" for more information about a command. Additional help topics: buildmode build modes c calling between Go and C cache build and test caching environment environment variables filetype file types go.mod the go.mod file gopath GOPATH environment variable gopath-get legacy GOPATH go get goproxy module proxy protocol importpath import path syntax modules modules, module versions, and more module-get module-aware go get packages package lists and patterns testflag testing flags testfunc testing functions Use "go help <topic>" for more information about that topic.
Poslední základní kontrola, kterou dnes provedeme, se týká nastavení proměnné prostředí GOPATH, o níž jsme se zmínili výše. Zobrazení aktuálně nastavené hodnoty této proměnné prostředí tak, jak ji vidí nástroje Go, zajistí příkaz:
$ go env GOPATH
Pro uživatele „tester“ dostaneme tento výstup:
/home/tester/go
7. První demonstrační příklad – klasický „Hello world!“ ve dvou variantách
Konečně se dostáváme k popisu samotné syntaxe a sémantiky programovacího jazyka Go. Již tradičně začínají podobně koncipované články a knihy programem, který po svém spuštění vypíše na terminál řetězec „Hello world!“ a následně se ukončí. V Go je možné tento příklad napsat následujícím způsobem (úplný a naformátovaný zdrojový kód získáte z adresy https://github.com/tisnik/go-root/blob/master/article01/01_hello_world.go):
package main func main() { println("Hello world!") }
Povšimněte si, že v programu explicitně specifikujeme jméno balíčku (zde „main“). Dále se ve zdrojovém kódu programu s využitím klíčového slova func deklaruje funkce nazvaná taktéž „main“. Tato funkce, která je současně vstupním bodem do aplikace, nemá žádné parametry ani návratovou hodnotu (více se o návratových hodnotách dozvíme v navazujících kapitolách) a voláme z ní funkci nazvanou println(). Jedná se o funkci dostupnou ze základní knihovny, jejíž možnosti jsou sice dosti zásadním způsobem omezeny, ovšem pro programy typu „Hello world!“ mohou být dostačující. A na závěr – za příkazem volání funkce není nutné psát středník (ve skutečnosti bude středník při přeformátování zdrojového kódu odstraněn).
První příklad si můžeme spustit a to konkrétně příkazem go run. Před samotným spuštěním se sice (potichu) provede překlad, přičemž spustitelná verze programu je uložena do dočasného souboru:
$ go run 01_hello_world.go Hello world!
Pokud si budeme přát program explicitně přeložit a zachovat výsledný binární soubor, použijeme namísto go run příkaz go build:
$ go build 01_hello_world.go
Výsledkem bude soubor pojmenovaný stejně jako soubor zdrojový, ovšem bez koncovky „.go“ (na Unixových systémech):
$ ls -l total 1072 -rwxrwxr-x. 1 tester tester 1067905 Nov 8 12:45 01_hello_world -rw-rw-r--. 1 tester tester 55 Nov 7 09:02 01_hello_world.go
Nově vytvořený soubor nazvaný „01_hello_world“ obsahuje jak přeložený nativní kód naší aplikace, tak i všechny potřebné funkce nutné pro spuštění aplikace (včetně automatického správce paměti). Právě kvůli tomu, že se v tomto binárním souboru nachází i všechny potřebné runtime funkce, je soubor tak velký (cca 1,1MB, oproti tomu aplikace se stejnou funkcí naprogramovaná v C bude mít po překladu velikost 6 až 14 kB v závislosti na architektuře, v assembleru se na platformě x86–64 dostaneme na 504 bajtů).
Právě přeloženou aplikaci si můžeme velmi snadno spustit následujícím příkazem:
$ ./01_hello_world Hello world!
Pokud budete chtít prozkoumat, jaké symboly se vlastně v souboru „01_hello_world“ nachází (je jich zhruba 1300!, protože je zde skutečně celý runtime), můžeme použít příkaz objdump:
$ objdump -t 01_hello_world
Ve druhém příkladu, který naleznete na adrese https://github.com/tisnik/go-root/blob/master/article01/02_better_hello_world.go, používáme namísto dosti primitivní funkce println univerzálnější funkci Println. Tato funkce se nachází v balíčku fmt, který je nutné explicitně naimportovat a funkci plně kvalifikovat:
package main import "fmt" func main() { fmt.Println("Hello world!") }
Opět si ukažme způsob tichého překladu a spuštění tohoto příkladu:
$ go run 02_better_hello_world.go Hello world!
8. Jak pracovat s dokumentací
Dokumentace k jazyku Go i ke standardním knihovnám je umístěna na adrese https://godoc.org/. V předchozích příkladech jsme používali balíček „fmt“, který je součástí standardní knihovny jazyka Go a je samozřejmě taktéž na výše zmíněných stránkách popsán: https://godoc.org/fmt.
Kromě toho je však možné si nechat dokumentaci zobrazit přímo v terminálu při vývoji. K tomu slouží příkaz go doc, kterému se jako další parametr předá jméno balíčku popř. přímo jméno funkce, jejíž popis potřebujeme získat:
$ go doc builtin
Výsledek bude na terminálu zobrazen následujícím způsobem:
package builtin // import "builtin" Package builtin provides documentation for Go's predeclared identifiers. The items documented here are not actually in package builtin but their descriptions here allow godoc to present documentation for the language's special identifiers. ... ... ... func print(args ...Type) func println(args ...Type)
Samozřejmě je možné použít utility pro stránkování – less, more atd.:
$ go doc fmt | less
S výstupem:
package fmt // import "fmt" Package fmt implements formatted I/O with functions analogous to C's printf and scanf. The format 'verbs' are derived from C's but are simpler. Printing The verbs: General: %v the value in a default format when printing structs, the plus flag (%+v) adds field names %#v a Go-syntax representation of the value %T a Go-syntax representation of the type of the value %% a literal percent sign; consumes no value
Pokud je zapotřebí zobrazit dokumentaci k funkci z nějakého balíčku, použije se následující styl zápisu:
$ go doc fmt.Println
S výstupem:
func Println(a ...interface{}) (n int, err error) Println formats using the default formats for its operands and writes to standard output. Spaces are always added between operands and a newline is appended. It returns the number of bytes written and any write error encountered.
9. Doporučovaný styl zápisu programů
Tvůrci programovacího jazyka Go navrhli, jakým způsobem se mají formátovat zdrojové kódy. Nezůstalo ovšem pouze u doporučení, protože přímo v základní sadě nástrojů jazyka Go nalezneme i utilitku nazvanou příznačně gofmt. Tento nástroj slouží k přeformátování zdrojových kódů takovým způsobem, aby jejich výsledný tvar odpovídal specifikaci. Díky tomu mají (nebo by alespoň měly mít) všechny zdrojové kódy napsané v Go jednotný standardní formát a například diskuze o použití tabů nebo mezer, popř. o kolik znaků se má provést odsazení bloků, přestávají mít smysl (o to jsou ale zábavnější).
10. Nástroj gofmt určený pro přeformátování zdrojových kódů
Pokud jste provedli instalaci Go a jeho nástrojů podle pokynů ze čtvrté kapitoly, bude nástroj gofmt jednoduše použitelný:
$ gofmt --help
Tento nástroj podporuje různé volby, ovšem v praxi využijete pravděpodobně především volbu -w pro zápis přeformátovaného zdrojového kódu do jiného souboru:
usage: gofmt [flags] [path ...] -cpuprofile string write cpu profile to this file -d display diffs instead of rewriting files -e report all errors (not just the first 10 on different lines) -l list files whose formatting differs from gofmt's -r string rewrite rule (e.g., 'a[b:len(a)] -> a[b:]') -s simplify code -w write result to (source) file instead of stdout
Zkusme například formátovači předat následující zdrojový kód se středníky, špatným odsazením, prázdnými řádky atd.:
package main import "fmt"; func main() { fmt.Println("Hello, playground"); }
Po naformátování získáme tento obsah, který plně odpovídá specifikaci:
package main import "fmt" func main() { fmt.Println("Hello, playground") }
11. Kontrola použití importovaných modulů
Ve třetím příkladu, jehož zdrojový kód naleznete na adrese https://github.com/tisnik/go-root/blob/master/article01/03_unused_imports.go, je předchozí zdrojový kód upraven takovým způsobem, že namísto funkce fmt.Println opět použijeme standardní funkci println, ovšem ponecháme deklaraci importu „fmt“:
package main import "fmt" func main() { println("Hello world!") }
Z pohledu překladače programovacího jazyka Go se jedná o potenciální chybu, na kterou taktéž standardním způsobem upozorní:
$ go run 03_unused_imports.go # command-line-arguments ./03_unused_imports.go:3:8: imported and not used: "fmt"
12. Plná podpora Unicode v programech
Další vlastnost programovacího jazyka Go by nás neměla překvapit. Jedná se o plnohodnotnou podporu Unicode a taktéž kódování UTF-8. Proč není tato vlastnost překvapivá? Za vznikem UTF-8 totiž stojí právě Rob Pike a Ken Thompson, shodou okolností dva ze tří návrhářů programovacího jazyka Go. Ukažme si, že se UTF-8 může používat přímo ve zdrojovém kódu čtvrtého demonstračního příkladu:
package main import "fmt" func main() { fmt.Println("╭─────────────────────╮") fmt.Println("│ příλiš žλuťΩučký kůň│") fmt.Println("╰─────────────────────╯") }
Tento program půjde bez problémů přeložit i spustit (za předpokladu, že je správně nastavený terminál, což by snad v 21.století nemusel být problém):
$ go run 04_hello_unicode.go ╭─────────────────────╮ │ příλiš žλuťΩučký kůň│ ╰─────────────────────╯
13. Základy typového systému – odvození typu proměnné z hodnoty
Podobně jako v mnoha dalších kompilovaných programovacích jazycích nalezneme i v jazyce Go velké množství základních datových typů, které do určité míry odráží vlastnosti mikroprocesorů, na kterých budou aplikace provozovány (předpokládají se dnes převažující architektury s osmibitovými, 16bitovými, 32bitovými a 64bitovými slovy, ne některé speciální DSP atd.). Kromě numerických typů patří mezi základní datové typy i řetězce (ty jsou neměnitelné – immutable), pole (array), takzvané řezy (slice) a mapy (map). V Go nalezneme i ukazatele, ovšem jejich význam je oproti C či C++ do značné míry omezen. Nové datové typy jsou samozřejmě uživatelsky definovatelné, což je téma, kterému se budeme podrobněji věnovat příště.
Zajímavé a velmi užitečné je, že Go dokáže odvodit typ proměnné z hodnoty, která je proměnné přiřazena při její deklaraci. Pro přiřazení se v takovém případě používá operátor := (short variable declaration) a u proměnné se nemusí deklarovat její typ (úplný zdrojový kód):
package main import "fmt" func main() { a := 10 fmt.Println(a) b := "hello" fmt.Println(b) c := true fmt.Println(c) }
Výsledek po spuštění programu:
$ go run 05_basic_type_inference.go 10 hello true
Jakmile je proměnná deklarována, není ji možné v daném bloku deklarovat znovu, tudíž ani není možné použít znovu přiřazení zapisované operátorem :=. Můžeme si to snadno otestovat na dalším příkladu:
package main import "fmt" func main() { a := 10 fmt.Println(a) b := "hello" fmt.Println(b) c := true fmt.Println(c) a := "world" fmt.Println(a) b := 0 fmt.Println(b) c := nil fmt.Println(c) }
V tomto případě dojde k chybě při překladu:
$ go run 06_variable_redeclaration.go # command-line-arguments ./06_variable_redeclaration.go:13:4: no new variables on left side of := ./06_variable_redeclaration.go:15:4: no new variables on left side of := ./06_variable_redeclaration.go:17:4: no new variables on left side of :=
Pokud ovšem namísto operátoru := použijeme běžné přiřazení, bude zdrojový kód bez problému přeložitelný (a bude samozřejmě korektní i po stránce sémantiky):
package main import "fmt" func main() { a := 10 fmt.Println(a) b := "hello" fmt.Println(b) c := true fmt.Println(c) a = 20 fmt.Println(a) b = "world" fmt.Println(b) c = false fmt.Println(c) }
Výsledek po překladu a spuštění:
$ go run 07_variable_reassign.go 10 hello true 20 world false
14. Kontrola typu hodnoty přiřazované do proměnné
Díky silnému typovému systému je pochopitelně odhalena snaha o přiřazení hodnoty jiného typu do již deklarované proměnné, což si ověříme na dalším příkladu:
package main import "fmt" func main() { a := 10 fmt.Println(a) b := "hello" fmt.Println(b) c := true fmt.Println(c) a = "world" fmt.Println(a) b = 0 fmt.Println(b) c = nil fmt.Println(c) }
Výsledek snahy o překlad:
$ go run 08_no_true_dynamic_type.go # command-line-arguments ./08_no_true_dynamic_type.go:13:6: cannot use "world" (type string) as type int in assignment ./08_no_true_dynamic_type.go:15:4: cannot use 0 (type int) as type string in assignment ./08_no_true_dynamic_type.go:17:4: cannot use nil as type bool in assignment
Tento příklad byl sice velmi jednoduchý, ale podobně bude typová kontrola fungovat i při použití uživatelských typů, polí, řezů atd.
15. Deklarace funkcí
S deklarací funkce jsme se již setkali (jednalo se o funkci main), takže si jen krátce ukážeme, jak je možné nadeklarovat další funkci bez parametrů a bez návratové hodnoty. Testovaná funkce se bude jmenovat printHello. Vzhledem k tomu, že funkce neakceptuje žádné parametry, uvedou se za jejím názvem pouze prázdné kulaté závorky a za nimi již začíná tělo funkce uvozené levou složenou závorkou. Při volání funkce bez parametrů se opět použijí prázdné kulaté závorky:
package main import "fmt" func printHello() { fmt.Println("Hello world!") } func main() { printHello() }
Tato funkce je volána z funkce main, pochopitelně bez parametrů:
$ go run 09_simple_function.go Hello world!
16. Specifikace typu argumentů funkcí
Ukažme si nyní, jak se specifikuje typ parametru či parametrů funkce. Na rozdíl od jazyků C, C++ či Java se pro specifikaci parametrů používá zápis „jméno parametru [mezera] datový typ“. Volání funkce s parametrem/parametry se již nijak výrazně neodlišuje od jiných mainstreamových programovacích jazyků:
package main import "fmt" func printMessage(message string) { fmt.Println(message) } func main() { printMessage("Hello world!") }
Překlad a spuštění tohoto demonstračního příkladu:
$ go run 10_function_with_params.go Hello world!
17. Příkaz return
Funkce, které tvoří základní stavební prvek programů psaných v Go (ostatně i metody jsou zde chápány jako funkce), samozřejmě mohou vracet nějakou návratovou hodnotu. Její typ se uvede za pravou uzavírací kulatou závorku se specifikací parametrů funkce. Přímo ve funkci je možné použít příkaz return, ovšem existují i další možnosti, s nimiž se seznámíme později. Podívejme se, jak může vypadat deklarace funkce bez parametrů, která ovšem vrací hodnotu typu řetězec (string):
func getMessage() string { return "Hello world!" }
Příklad s voláním takové funkce:
package main import "fmt" func getMessage() string { return "Hello world!" } func printMessage(message string) { fmt.Println(message) } func main() { printMessage(getMessage()) }
Tento demonstrační příklad po svém spuštění opět vypíše zprávu „Hello world!“:
$ go run 11_return_statement.go Hello world!
Alternativně je možné pojmenovat výstupní parametr (parametry) a vlastní určení, jaká hodnota se má vrátit, bude provedeno prostým přiřazením (což se vzdáleně podobná způsobu, jakým pracují funkce v Pascalu):
package main import "fmt" func getMessage() (message string) { message = "Hello world!" return } func printMessage(message string) { fmt.Println(message) } func main() { printMessage(getMessage()) }
Po příkazu return by se již neměl použít žadný další příkaz, což je kontrolováno překladačem:
package main import "fmt" func getMessage() (message string) { message = "Hello world!" return x := 42 } func printMessage(message string) { fmt.Println(message) } func main() { printMessage(getMessage()) }
Otestujme si, zda překladač skutečně ohlídá, že se za return už žádný další příkaz nepoužije:
$ go run 16_return_at_the_end_of_function.go # command-line-arguments ./16_return_at_the_end_of_function.go:9:1: missing return at end of function
18. Vrácení většího množství hodnot z funkce
V programovacím jazyce Go se velmi často setkáme s tím, že funkce vrací volajícímu kódu větší množství návratových hodnot. Mnoho funkcí ze standardní knihovny například vrací vlastní požadovanou hodnotu a taktéž kód chyby; toto chování je ovšem doporučeno používat i v ostatních modulech. Podívejme se na základní příklad, v němž funkce swap akceptuje dva parametry a vrací je, ovšem v opačném pořadí. Kód pracující s oběma návratovými hodnotami je zvýrazněn:
package main import "fmt" func swap(a int, b int) (int, int) { return b, a } func main() { x := 1 y := 2 var z int var w int z, w = swap(x, y) fmt.Println("z =", z) fmt.Println("w =", w) }
Výsledek po překladu a spuštění tohoto demonstračního příkladu:
$ go run 14_swap_function.go z = 2 w = 1
Zajímavé bude propojení dvou vlastností programovacího jazyka Go: automatického určení typu proměnné při její deklaraci na základě přiřazované hodnoty a možnosti vrátit větší množství hodnot z volané funkce. Povšimněte si zejména zvýrazněného řádku, na němž jsou deklarovány dvě lokální proměnné z a w, je jim přiřazen typ a taktéž přímo hodnota získaná voláním funkce swap:
package main import "fmt" func swap(a int, b int) (int, int) { return b, a } func main() { x := 1 y := 2 z, w := swap(x, y) fmt.Println("z =", z) fmt.Println("w =", w) }
Opět si ukažme výsledek po překladu a spuštění upraveného demonstračního příkladu:
$ go run 15_swap_function_B.go z = 2 w = 1
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - How the Go runtime implements maps efficiently (without generics)
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/ - 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings