Obsah
1. Programovací jazyk Go pro skalní céčkaře (2.část)
4. Neměnitelné a měnitelné řetězce
5. Problematika znaků zakódovaných v UTF-8
6. Porovnání řetězců bez jazykových specifik
7. Porovnání řetězců na základě jazykových specifik (speciálně pro ČR)
9. Získání podřetězce (ASCII znaky)
10. Podřetězce a Unicode znaky
14. Realokace polí resp. použití řezů
15. Sledování kapacity řezu při realokacích polí
16. Práce se soubory (základní vstupně-výstupní operace)
18. Zpracování chyb, reakce na chyby
19. Repositář s demonstračními příklady
1. Programovací jazyk Go pro skalní céčkaře (2.část)
V dnešním článku o programovacím jazyku Go dokončíme téma, kterému jsme se začali věnovat minule. Opět si ukážeme některé vlastnosti programovacího jazyka Go, které mohou překvapit vývojáře používající programovací jazyk C (a v menší míře i C++) a kteří se snaží používat i jazyk Go, například pro nové projekty. Některé vlastnosti programovacího jazyka Go mohou být pro nováčky poměrně matoucí, což se týká například práce s řetězci (rozdíl mezi chápáním řetězců jako sekvence bajtů a znaků). Další vlastnosti naopak vedou k tvorbě kratších, bezpečnějších a popř. i lépe pochopitelných zdrojových kódů, zejména ve chvíli, kdy se pracuje s dynamicky alokovanou pamětí, poli, spojování a rozdělování řetězců atd. Zcela odlišné jsou jazyky Go a C při práci se soubory resp. přesněji řečeno se vstupně-výstupním systémem a knihovnami, které I/O realizují. Odlišný je i systém pro detekci a zpracování chyb, které mohou při běhu aplikace nastat.
2. Pravdivostní hodnoty
Popis rozdílů mezi programovacím jazykem C a jazykem Go začneme u zdánlivě zcela triviálního tématu – pravdivostních hodnot, způsobu jejich reprezentace a (automatických) konverzí. V céčku je za pravdu považována jakákoli nenulová hodnota, což platí jak pro celočíselné hodnoty i hodnoty s plovoucí řádovou čárkou, tak i pro ukazatele. To například znamená, že následující demonstrační příklad po svém překladu, slinkování a spuštění postupně vypíše zprávy:
false true true false
Nejprve se vyhodnocuje podmínka s nulovým ukazatelem, dále podmínka s celým číslem, na třetím řádku podmínka s hodnotou s plovoucí řádovou čárkou a nakonec test ukazatele inicializovaného opět na hodnotu NULL:
#include <stdio.h> int main(void) { if (NULL) { puts("true"); } if (!NULL) { puts("false"); } if (1) { puts("true"); } if (0) { puts("false"); } if (1.5) { puts("true"); } if (0.0) { puts("false"); } void *b1 = NULL; if (b1) { puts("true"); } if (!b1) { puts("false"); } return 0; }
V programovacím jazyce Go je ovšem situace zcela odlišná, protože v tomto jazyku existují pouze dvě pravdivostní hodnoty – true a false, které jsou typu bool. Žádné další hodnoty se nesmí v rozhodovací konstrukci if, v podmínce smyčky for atd., vyskytovat. To například znamená, že následující demonstrační příklad nebude možné přeložit (a tudíž pochopitelně ani spustit):
package main func main() { if nil { println("true") } if !nil { println("false") } if 1 { println("true") } if 0 { println("false") } if 1.5 { println("true") } if 0.0 { println("false") } var b1 bool = nil if b1 { println("true") } if !b1 { println("false") } }
Při pokusu o překlad se vypíšou tyto zprávy o chybě:
./01_booleans.go:4:2: use of untyped nil ./01_booleans.go:8:5: invalid operation: ! nil ./01_booleans.go:12:2: non-bool 1 (type int) used as if condition ./01_booleans.go:16:2: non-bool 0 (type int) used as if condition ./01_booleans.go:20:2: non-bool 1.5 (type float64) used as if condition ./01_booleans.go:24:2: non-bool 0 (type float64) used as if condition ./01_booleans.go:28:6: cannot use nil as type bool in assignment
package main import "fmt" func main() { var a int = 0 if a = 2 { fmt.Println("==2") } }
Pokus o překlad skončí s chybou:
t.go:7:5: expected boolean expression, found assignment (missing parentheses around composite literal?)
Podobně není umožněna automatická konverze numerické hodnoty popř. řetězce na hodnotu pravdivostní:
package main import "fmt" func main() { var a bool = true var b bool = false var c bool = 0 var d bool = "" fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) }
Opět se můžeme přesvědčit, že tyto chyby odhalí překladač:
./02_boolean_type_checks.go:8:6: cannot use 0 (type int) as type bool in assignment ./02_boolean_type_checks.go:9:15: cannot use "" (type string) as type bool in assignment
#include <stdio.h> int main(void) { if ("") { puts("true"); } else { puts("false"); } if ("foobar") { puts("true"); } else { puts("false"); } return 0; }
Jak jsme si již napsali v předchozím odstavci, překladač celou podmínku většinou odstraní (a to u většiny překladačů nezávisle na povolení či zákazu optimalizací):
if ("") { puts("true"); 11: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0 18: e8 fc ff ff ff call 19 <main+0x19> } else { puts("false"); }
3. Zpracování řetězců
Poněkud větší rozdíly mezi programovacími jazyky C a Go panují v oblasti zpracování řetězců. Nejprve se podívejme, jak se v C zpracovávají konstantní (neměnné) řetězce, které jsou na moderních architekturách umístěny v segmentu nastaveném pouze pro čtení (sekce se tedy jmenuje .rodata). Takové řetězce jsou v jazyku C představovány řetězcovým literálem. Aby byl příklad poněkud komplikovanější, obsahuje poslední řetězcový literál Unicode znaky, které jsou reprezentovány v kódu UTF-8 proměnným počtem bajtů (zde tedy neplatí, že každý znak reprezentuje jediný znak). Tento příklad bude správně přeložen jen na těch systémech, které jsou nakonfigurovány pro zpracování zdrojových kódů v UTF-8 (ovšem jedná se o poměrně složité téma, o kterém se dozvíte více informací například na stránce Unicode in C and C++: What You Can Do About It Today, pro naše účely však bude dostačovat mít možnost vypsat řetězec reprezentovaný v UTF-8):
#include <stdio.h> int main(void) { char *s1 = "www.root.cz"; char *s2 = ""; char *s3 = "Hello\nworld!\n"; char *s4 = "[шщэюя]"; puts(s1); puts(s2); puts(s3); puts(s4); return 0; }
.file "03_strings.c" .text .Ltext0: .section .rodata .LC0: .string "www.root.cz" .LC1: .string "" .LC2: .string "Hello\nworld!\n" .LC3: .string "[\321\210\321\211\321\215\321\216\321\217]" .text .globl main .type main, @function
Sekvenci bajtů s kódy 321 210 321 211 321 215 321 216 321 217 v osmičkové soustavě si převedeme do desítkové soustavy a nalezneme příslušné znaky v Unicode (konkrétně budeme hledat v bloku Cyrillic:
Bajty (octal) | Bajty (dec) | Unicode index | Index v hexa | Znak |
---|---|---|---|---|
321 210 | 209 136 | 1096 | 0×0448 | ш |
321 211 | 209 137 | 1097 | 0×0449 | щ |
321 215 | 209 141 | 1101 | 0×044d | э |
321 216 | 209 142 | 1102 | 0×044e | ю |
321 217 | 209 143 | 1103 | 0×044f | я |
V programovacím jazyku Go budeme postupovat prakticky stejným způsobem, protože zde jsou řetězce taktéž neměnné a opět se může použít UTF-8 pro Unicode znaky (zde je ovšem podpora UTF-8 přímo součástí specifikace programovacího jazyka):
package main import "fmt" func main() { var s1 string = "www.root.cz" var s2 string = "" var s3 string = "Hello\nworld!\n" var s4 string = "шщэюя" fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println(s4) }
Existuje ovšem i kratší způsob, v němž je typ proměnné odvozen od typu hodnoty, která se do proměnné vkládá. Musíme zde použít operátor := a nikoli pouze rovnítko:
package main import "fmt" func main() { s1 := "www.root.cz" s2 := "" s3 := "Hello\nworld!\n" s4 := "шщэюя" fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println(s4) }
4. Neměnitelné a měnitelné řetězce
Řetězcové literály jsou většinou uloženy v kódovém segmentu (též známý jako textový segment, viz též předchozí kapitolu), který je na většině platforem určen pouze pro čtení (výjimkou jsou například osmibitové mikropočítače, x86 v reálném režimu DOSu apod.). To znamená, že by se správně měl použít modifikátor const, aby se následující (jinak obecně chybný kód, jehož chování je navíc podle normy nespecifikováno) nemohl přeložit:
#include <stdio.h> int main(void) { const char *s = "www.root.cz"; puts(s); s[3] = '*'; s[8] = '*'; puts(s); return 0; }
Pokus o překlad skutečně skončí s chybou:
04_immutable_strings.c: In function ‘main’: 04_immutable_strings.c:7:10: error: assignment of read-only location ‘*(s + 3)’ s[3] = '*'; ^ 04_immutable_strings.c:8:10: error: assignment of read-only location ‘*(s + 8)’ s[8] = '*'; ^
Pokud vynecháme modifikátor const a podíváme se do vygenerovaného strojového kódu, uvidíme pokus o změnu obsahu segmentu, který je určen jen pro čtení (sekce .rodata):
char *s = "www.root.cz"; 11: c7 45 f8 00 00 00 00 mov DWORD PTR [ebp-0x8],0x0 s[3] = '*'; 23: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8] 26: 83 c0 03 add eax,0x3 29: c6 00 2a mov BYTE PTR [eax],0x2a s[8] = '*'; 2c: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8] 2f: 83 c0 08 add eax,0x8 32: c6 00 2a mov BYTE PTR [eax],0x2a
Samotný řetězec je skutečně uložen v sekci rodata:
Contents of section .rodata: 0000 7777772e 726f6f74 2e637a00 www.root.cz.
V programovacím jazyce Go jsou řetězce neměnné (immutable), takže ani zde nebude možné tento zdrojový kód přeložit a spustit:
package main import "fmt" func main() { var s string = "www.root.cz" fmt.Println(s) s[3] = '*' s[8] = '*' fmt.Println(s) }
Výsledek spuštění tohoto příkladu:
./04_immutable_strings.go:9:7: cannot assign to s[3] ./04_immutable_strings.go:10:7: cannot assign to s[8]
Pokud v jazyku C budeme chtít vytvořit modifikovatelný řetězec, lze použít tento postup – namísto ukazatele na první znak řetězcového literálu vytvoříme pole a celý řetězec do něj uložíme:
#include <stdio.h> int main(void) { char s[] = "www.root.cz"; puts(s); s[3] = '*'; s[8] = '*'; puts(s); return 0; }
char s[] = "www.root.cz"; 1c: c7 45 ec 77 77 77 2e mov DWORD PTR [ebp-0x14],0x2e777777 23: c7 45 f0 72 6f 6f 74 mov DWORD PTR [ebp-0x10],0x746f6f72 2a: c7 45 f4 2e 63 7a 00 mov DWORD PTR [ebp-0xc],0x7a632e
Na 64bitové platformě x86–64 se počet instrukcí ještě dále snižuje na pouhé dva přesuny:
char s[] = "www.root.cz"; 17: 48 b8 77 77 77 2e 72 movabs rax,0x746f6f722e777777 1e: 6f 6f 74 21: 48 89 45 e0 mov QWORD PTR [rbp-0x20],rax 25: c7 45 e8 2e 63 7a 00 mov DWORD PTR [rbp-0x18],0x7a632e
V jazyku Go se můžeme k výše popsanému chování přiblížit tak, že použijeme pole bajtů a nikoli přímo řetězec (ten bude sloužit jako zdroj dat pro naplnění pole, konverze řetězce na bajty a alokace se pochopitelně provede automaticky):
package main import "fmt" func main() { var s []byte = []byte("www.root.cz") fmt.Println(string(s)) s[3] = '*' s[8] = '*' fmt.Println(string(s)) }
Výsledek spuštění tohoto příkladu:
www.root.cz www*root*cz
5. Problematika znaků zakódovaných v UTF-8
Ještě jednou se vraťme k problematice znaků v řetězcích s kódováním UTF-8. Jednotlivé znaky jsou reprezentovány jedním až čtyřmi bajty, takže není snadné říci, zda přepisem jednoho bajtu nedojde k „rozbití“ jednoho nebo i dvou znaků ve výsledném řetězci. V následujícím zdrojovém kódu nahrazujeme první a poslední znak hvězdičkou, což je v pořádku, protože tyto dva znaky patří do původní sady ASCII a jsou tedy reprezentovány jediným bajtem. Ovšem současně přepisujeme i bajt (ne znak!) na indexu 5, což není korektní, protože na této pozici se nachází jeden ze dvou bajtů reprezentujících znak v azbuce:
#include <stdio.h> int main(void) { char s[] = "[шщэюя]"; puts(s); s[0] = '*'; /* problematicka cast */ s[5] = '-'; s[11] = '*'; puts(s); return 0; }
Výsledek spuštění příkladu:
[шщэюя] *шщ-�юя*
Tímto způsobem můžeme s řetězci (většinou nesprávně) manipulovat i v programovacím jazyce Go:
package main import "fmt" func main() { var s []byte = []byte("[шщэюя]") fmt.Println(string(s)) s[0] = '*' // problematicka cast s[5] = '-' s[11] = '*' fmt.Println(string(s)) }
Výsledek spuštění příkladu:
[шщэюя] *шщ-�юя*
Korektní způsob, který však nemusí být příliš rychlý, spočívá v převodu řetězce na pole run a v manipulaci s obsahem tohoto pole:
package main import "fmt" func main() { s := "[шщэюя]" var r []rune = []rune(s) fmt.Println(string(r)) r[0] = '*' r[3] = '-' r[6] = '*' fmt.Println(string(r))
Nyní již výsledek bude odpovídat očekávání:
[шщэюя] *шщ-юя*
6. Porovnání řetězců bez jazykových specifik
Dále se budeme zabývat porovnáváním řetězců. V programovacím jazyku C pochopitelně není možné použít pro přímé porovnání řetězců běžné relační operátory, protože ty by porovnaly pouze hodnoty ukazatelů a nikoli obsah řetězců. Namísto toho se používají funkce strcmp (pro „úzké“ znaky) popř. wcscmp (pro „široké“ znaky). Tato funkce vrací záporné číslo, pokud je první řetězec lexikograficky menší, než řetězec druhý. Pokud jsou oba řetězce stejné, vrací se nula a kladné číslo v případě, že je první řetězec lexikograficky větší, než druhý.
Vyzkoušejme si nyní, jak toto porovnání funguje v praxi, a to i při použití Unikódu, resp. přesněji řečeno řetězců se znaky zakódovanými v UTF-8:
#include <stdio.h> #include <string.h> int main(void) { printf("%d\n", strcmp("aa", "ab")); printf("%d\n", strcmp("aa", "aa")); printf("%d\n", strcmp("e", "é")); printf("%d\n", strcmp("e", "ě")); printf("%d\n", strcmp("ě", "é")); printf("%d\n", strcmp("z", "é")); printf("%d\n", strcmp("z", "ě")); printf("%d\n", strcmp("h", "ch")); printf("%d\n", strcmp("ch", "i")); printf("%d\n", strcmp("Hrdina", "Chocholoušek")); printf("%d\n", strcmp("Crha", "Chocholoušek")); return 0; }
Výsledky nejsou v tomto případě korektní (a ani být nemohou):
-1 strcmp("aa", "ab"); 0 strcmp("aa", "aa"); -1 strcmp("e", "é"); -1 strcmp("e", "ě"); 1 strcmp("ě", "é"); -1 strcmp("z", "é"); -1 strcmp("z", "ě"); 1 strcmp("h", "ch"); -1 strcmp("ch", "i"); 1 strcmp("Hrdina", "Chocholoušek"); 1 strcmp("Crha", "Chocholoušek");
Povšimněte si, že je porovnání korektní v prvních dvou případech, ovšem již ne pro znaky s nabodeníčky. Další problém (lokální) spočívá v tom, že „ch“ není chápán jako samostatný znak ležící mezi „h“ a „i“.
V jazyce Go můžeme podobné porovnání provést přímo s využitím relačních operátorů. Jinými slovy – relační operátory jsou korektně aplikovatelné i na řetězce (pokud na chvíli zapomeneme na problémy se znaky s nabodeníčky atd., což je téma, kterým se budeme podrobněji zabývat v navazující kapitole):
package main import "fmt" func main() { fmt.Println("aa" < "ab") fmt.Println("aa" == "ab") fmt.Println("aa" < "aa") fmt.Println("aa" == "aa") fmt.Println("e" < "é") fmt.Println("e" < "ě") fmt.Println("z" < "é") fmt.Println("z" < "ě") fmt.Println("h" < "ch") fmt.Println("ch" < "i") fmt.Println("Hrdina" < "Chocholoušek") fmt.Println("Crha" < "Chocholoušek") }
V případě, že je nutné získat výsledky porovnání v numerické podobě (-1, 0, 1), což je vyžadováno například u některých řadicích algoritmů, je možné použít funkci strings.Compare:
package main import "fmt" import "strings" func main() { fmt.Println(strings.Compare("aa", "ab")) fmt.Println(strings.Compare("aa", "aa")) fmt.Println(strings.Compare("e", "é")) fmt.Println(strings.Compare("e", "ě")) fmt.Println(strings.Compare("ě", "é")) fmt.Println(strings.Compare("z", "é")) fmt.Println(strings.Compare("z", "ě")) fmt.Println(strings.Compare("h", "ch")) fmt.Println(strings.Compare("ch", "i")) fmt.Println(strings.Compare("Hrdina", "Chocholoušek")) fmt.Println(strings.Compare("Crha", "Chocholoušek")) }
S výsledky shodnými s původním céčkovým kódem:
-1 0 -1 -1 1 -1 -1 1 -1 1 1
7. Porovnání řetězců na základě jazykových specifik (speciálně pro ČR)
V případě, že budeme chtít provést zcela korektní porovnání řetězců, které bude brát v úvahu všechna jazyková specifika, už si nevystačíme se základními operátory < ani s funkcí strings.Compare. Namísto toho je nutné správně nastavit jazyk (Czech, Slovak atd.) a použít funkci CompareString z balíčku text/collate (ten si nainstalujete příkazem go get). V následujícím demonstračním příkladu je ukázáno, že pro jazyk language.Czech je k dispozici porovnání, které například pracuje korektně se znakem „Ch“:
package main import "golang.org/x/text/collate" import "golang.org/x/text/language" import "fmt" func main() { cl := collate.New(language.Czech) fmt.Println(cl.CompareString("aa", "ab")) fmt.Println(cl.CompareString("aa", "aa")) fmt.Println(cl.CompareString("e", "é")) fmt.Println(cl.CompareString("e", "ě")) fmt.Println(cl.CompareString("ě", "é")) fmt.Println(cl.CompareString("z", "é")) fmt.Println(cl.CompareString("z", "ě")) fmt.Println(cl.CompareString("h", "ch")) fmt.Println(cl.CompareString("ch", "i")) fmt.Println(cl.CompareString("Hrdina", "Chocholoušek")) fmt.Println(cl.CompareString("Crha", "Chocholoušek")) }
Výsledky porovnání jsem doplnil i o odpovídající kód. Hodnota –1 znamená „menší než“:
-1 fmt.Println(cl.CompareString("aa", "ab")) 0 fmt.Println(cl.CompareString("aa", "aa")) -1 fmt.Println(cl.CompareString("e", "é")) -1 fmt.Println(cl.CompareString("e", "ě")) 1 fmt.Println(cl.CompareString("ě", "é")) 1 fmt.Println(cl.CompareString("z", "é")) 1 fmt.Println(cl.CompareString("z", "ě")) -1 fmt.Println(cl.CompareString("h", "ch")) -1 fmt.Println(cl.CompareString("ch", "i")) -1 fmt.Println(cl.CompareString("Hrdina", "Chocholoušek")) -1 fmt.Println(cl.CompareString("Crha", "Chocholoušek"))
8. Spojení řetězců
Tato kapitola bude relativně krátká, protože si v ní pouze ukážeme, jakým způsobem se provede spojení řetězců v programovacím jazyku C a v Go. V céčku je nutné pro nový řetězec alokovat dostatečnou kapacitu paměti, zkontrolovat, zda se alokace podařila, zkopírovat do nově alokované paměti první řetězec a připojit řetězec druhý. Na závěr je nutné provést i dealokaci nového řetězce ve chvíli, kdy již není zapotřebí. Samotný zdrojový kód je tedy relativně dlouhý a minimálně na třech místech mohou vzniknout chyby (alokace paměti bez započtení koncové nuly, kontrola, zda alokace proběhla v pořádku a dealokace paměti):
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { const char *s1 = "Hello "; const char *s2 = "world!"; size_t length = strlen(s1) + strlen(s2) + 1; char *s3 = (char *)calloc(length, sizeof(char)); if (s3) { strcpy(s3, s1); strcat(s3, s2); puts(s3); free(s3); } else { perror("calloc() failed"); } return 0; }
V případě programovacího jazyka Go je situace mnohem jednodušší, protože se o veškerou správu paměti postará samotný překladač a navíc je možné použít přetížený operátor + pro spojení řetězců:
package main import "fmt" func main() { s1 := "Hello " s2 := "world!" s := s1 + s2 fmt.Println(s) }
9. Získání podřetězce (ASCII znaky)
V případě, že pracujeme pouze s ASCII řetězci popř. s řetězci používajícím osmibitové znakové sady (ISO-8859–2, Windows-1250, starobylá KOI-8 atd.), je získání podřetězce v programovacím jazyku C na první pohled poměrně jednoduché:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { const char *s1 = "Hello world!"; char *s2 = (char*)calloc(4 + 2, sizeof(char)); strncpy(s2, s1 + 0, 4); puts(s2); return 0; }
V praxi je samozřejmě nutné opět provést kontrolu alokace, provést dealokaci řetězce apod.:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { const char *s1 = "Hello world!"; char *s2 = (char*)calloc(4 + 2, sizeof(char)); // sem patří kontrola alokace!!! ... strncpy(s2, s1 + 0, 4); puts(s2); // řetězec s2 je zapotřebí dealokovat ... return 0; }
V programovacím jazyku Go můžeme předchozí příklad implementovat následujícím způsobem, který ovšem bude plně funkční pouze pro ASCII řetězce, ve kterých platí, že index znaku je totožný s indexem příslušného bajtu:
package main import "fmt" func main() { s1 := "Hello world!" s2 := s1[0:4] fmt.Println(s2) }
10. Podřetězce a Unicode znaky
Jeden z problémů, na které dříve či později narazí každý vývojář začínající programovat v jazyku Go, je fakt, že některé operace s řetězci pracují s celými znaky, ovšem další operace řetězce zpracovávají jako pole bajtů. To samozřejmě způsobuje problémy, hlavně poté, co je aplikace „odladěna“ s využitím ASCII řetězců a následně se v reálném provozu použijí speciální znaky (může se to zdát divné, ovšem takové aplikace reálně vznikají). Ostatně stačí se podívat na následující příklad, v němž se snažíme z řetězce s pěti znaky (reprezentovaným polem devíti bajtů) přečíst postupně dva, tři či čtyři znaky pomocí operátoru „řezu“:
package main import "fmt" func main() { s := "шщэюя" fmt.Println(s[2:4]) fmt.Println(s[2:5]) fmt.Println(s[2:6]) fmt.Println() fmt.Println(s[3:4]) fmt.Println(s[3:5]) fmt.Println(s[3:6]) }
Výsledky jsou pochopitelně špatné, zejména ve druhém případě, kdy je ze znaku odstraněn poslední bajt a samotný kód je tedy nekorektní:
щ щ� щэ
V dalších třech případech, kdy se pokoušíme číst ze sudého bajtu, dostaneme ještě horší výsledky, protože první přečtený bajt netvoří celý znak, ale jen jeho část:
� �� �э
Existuje však jedna možnost, jak pracovat s celými znaky uloženými v řetězci. Nejdříve je nutné řetězec (sekvenci bajtů, kde každý znak obsazuje od jednoho do čtyř znaků) převést na pole „run“, což jsou hodnoty reprezentující plnohodnotný znak z Unicode. S takovým polem je již možné pracovat plnohodnotným způsobem, tedy vytvářet z něj řezy atd.:
package main import "fmt" func main() { s := "шщэюя" runes := []rune(s) fmt.Println(string(runes[2:4])) fmt.Println(string(runes[2:5])) fmt.Println(string(runes[2:6])) fmt.Println() fmt.Println(string(runes[3:4])) fmt.Println(string(runes[3:5])) fmt.Println(string(runes[3:6])) }
Výsledky budou nyní ve všech případech korektní:
эю эюя эюя ю юя юя
11. Vytvoření polí
Relativně jednoduchá situace je u polí, jen si musíme dát pozor na to, že v C nejsou prvky lokálního pole inicializovány:
#include <stdio.h> #include <stdint.h> int main(void) { int32_t a[10]; int i; puts("Pole pred upravou:"); for (i=0; i<10; i++) { printf(" %d", a[i]); } puts("\n"); for (i=0; i<10; i++) { a[i] = i * 2; } puts("Pole po uprave:"); for (i=0; i<10; i++) { printf(" %d", a[i]); } return 0; }
Původní obsah pole je z hlediska programátora náhodný:
Pole pred upravou: -1332340384 32764 0 0 4195888 0 4195472 0 -1332340160 32764 Pole po uprave: 0 2 4 6 8 10 12 14 16 18
Standard jazyka C ovšem inicializaci prvků umožňuje, pouze ho musíme explicitně zapsat (viz zvýrazněný kód):
#include <stdio.h> #include <stdint.h> int main(void) { int32_t a[10] = {0}; int i; puts("Pole pred upravou:"); for (i=0; i<10; i++) { printf(" %d", a[i]); } puts("\n"); for (i=0; i<10; i++) { a[i] = i * 2; } puts("Pole po uprave:"); for (i=0; i<10; i++) { printf(" %d", a[i]); } return 0; }
Výsledek:
Pole pred upravou: 0 0 0 0 0 0 0 0 0 0 Pole po uprave: 0 2 4 6 8 10 12 14 16 18
V jazyce Go jsou i prvky lokálního pole automaticky inicializovány na nulovou hodnotu (podle příslušného datového typu):
package main import "fmt" func main() { var a1 [10]byte var a2 [10]int32 a3 := [10]int32{1, 10, 2, 9, 3, 8, 4, 7, 5, 6} fmt.Printf("Delka pole 1: %d\n", len(a1)) fmt.Printf("Delka pole 2: %d\n", len(a2)) fmt.Printf("Delka pole 3: %d\n", len(a3)) var a [10]int fmt.Printf("Pole pred upravou: %v\n", a) for i := 0; i < len(a1); i++ { a[i] = i * 2 } fmt.Printf("Pole po uprave: %v\n", a) var matice [10][10]float32 fmt.Printf("Matice: %v\n", matice) }
Výsledky vypsané příkladem:
Delka pole 1: 10 Delka pole 2: 10 Delka pole 3: 10 Pole pred upravou: [0 0 0 0 0 0 0 0 0 0] Pole po uprave: [0 2 4 6 8 10 12 14 16 18] Matice: [[0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0]]
12. Kopie polí
V další krátké kapitole si jen připomeňme, že pole lze v jazyce Go kopírovat, takže vznikne pole nové, nezávislé na poli původním:
package main import "fmt" func main() { var a1 [10]int a2 := a1 fmt.Printf("Pole 1: %v\n", a1) fmt.Printf("Pole 2: %v\n", a2) for i := 0; i < len(a1); i++ { a1[i] = i * 2 } fmt.Printf("Pole 1: %v\n", a1) fmt.Printf("Pole 2: %v\n", a2) }
O tom, že modifikováno bylo jen původní pole a nikoli (i) jeho kopie, se lze snadno přesvědčit:
Pole 1: [0 0 0 0 0 0 0 0 0 0] Pole 2: [0 0 0 0 0 0 0 0 0 0] Pole 1: [0 2 4 6 8 10 12 14 16 18] Pole 2: [0 0 0 0 0 0 0 0 0 0]
13. Vícerozměrná pole
Zastavme se na chvíli u následujícího příkladu, který sice může vypadat uměle, ale s podobným konceptem se můžete setkat například v algoritmech pro práci s bitmapami atd. V příkladu je vytvořeno dvourozměrné pole 4×3 prvky a toto pole je modifikováno tak, že se mění pouze spodní index (horní je nulový). Překladač tak může vygenerovat jednodušší kód. Využívá se zde (přesně definované) vlastnosti polí: řádky jsou v paměti umístěny za sebou bez výplně:
#include <stdio.h> #include <stdint.h> int main(void) { int a[3][4]; int i, j; puts("Pole pred upravou:"); for (j=0; j<3; j++) { for (i=0; i<4; i++) { printf(" %2d", a[j][i]); } putchar('\n'); } puts("\n"); for (i=0; i<12; i++) { a[0][i] = i; } puts("Pole po uprave:"); for (j=0; j<3; j++) { for (i=0; i<4; i++) { printf(" %2d", a[j][i]); } putchar('\n'); } return 0; }
Příklad bude v C plně funkční:
Pole pred upravou: 1 0 4196029 0 -908827577 32764 0 0 4195952 0 4195472 0 Pole po uprave: 0 1 2 3 4 5 6 7 8 9 10 11
V jazyce Go ovšem tento algoritmus pracovat nebude, protože se meze indexů polí pro všechny dimenze kontroluje:
package main import "fmt" func main() { var a [3][4]int fmt.Printf("Pole pred upravou: %v\n", a) for i := 0; i < 12; i++ { a[0][i] = i } fmt.Printf("Pole po uprave: %v\n", a) }
Po spuštění příkladu dojde k běhové chybě:
panic: runtime error: index out of range [4] with length 4 goroutine 1 [running]: main.main() /home/tester/go-root/article_35/10_arrays_C.go:11 +0x1f3
14. Realokace polí resp. použití řezů
Za připomenutí stojí, že v programovacím jazyce Go se většinou můžeme vyhnout ručnímu (resp. přesněji řečeno explicitnímu) realokování pole, protože namísto (poměrně rigidních) polí lze využít řezy (slices) a funkci append, která do řezu přidá další prvek či prvky a v případě potřeby pole, nad nímž je řez postaven, realokuje automaticky:
package main import "fmt" func main() { var s0 []int s1 := []int{} s2 := make([]int, 0) s3 := make([]int, 10) fmt.Println(s0) fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println() s0 = append(s0, 1, 2, 3) s1 = append(s1, 1, 2, 3) s2 = append(s2, 1, 2, 3) s3 = append(s3, 1, 2, 3) fmt.Println(s0) fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) }
15. Sledování kapacity řezu při realokacích polí
To, jakým způsobem realokace probíhá, je řešeno interním algoritmem, který ovšem můžeme sledovat čtením kapacity řezu:
package main import "fmt" func main() { var slice []int for i := 1; i < 11; i++ { slice = append(slice, i) fmt.Printf("Slice: %v\n", slice) fmt.Printf("Slice length: %d\n", len(slice)) fmt.Printf("Slice capacity: %d\n\n", cap(slice)) } }
S výsledky:
Slice: [1] Slice length: 1 Slice capacity: 1 Slice: [1 2] Slice length: 2 Slice capacity: 2 Slice: [1 2 3] Slice length: 3 Slice capacity: 4 Slice: [1 2 3 4] Slice length: 4 Slice capacity: 4 Slice: [1 2 3 4 5] Slice length: 5 Slice capacity: 8 Slice: [1 2 3 4 5 6] Slice length: 6 Slice capacity: 8 Slice: [1 2 3 4 5 6 7] Slice length: 7 Slice capacity: 8 Slice: [1 2 3 4 5 6 7 8] Slice length: 8 Slice capacity: 8 Slice: [1 2 3 4 5 6 7 8 9] Slice length: 9 Slice capacity: 16 Slice: [1 2 3 4 5 6 7 8 9 10] Slice length: 10 Slice capacity: 16
16. Práce se soubory (základní vstupně-výstupní operace)
Vzhledem k tomu, že se základní knihovny pro I/O operace v jazyku C a Go velmi odlišují, bude zcela odlišné například načítání dat ze souborů, jejich ukládání, práce s bloky atd.
Prozatím byla většina kódů psaná v Go v porovnání s C kratší, ovšem například při čtení ze souboru znak po znaku (přesněji bajt po bajtu) bude céčková varianta nepatrně kratší díky tomu, že lze použít funkce fgetc:
#include <stdio.h> #include <stdint.h> const char* filename = "test_input.txt"; int main(void) { FILE *fin = fopen(filename, "r"); if (!fin) { perror("Can not open file"); return 1; } char c; while ((c=fgetc(fin)) != EOF) { printf("%02d ", c); } fclose(fin); return 0; }
Výsledky (kódy jednotlivých znaků):
108 105 110 101 32 35 49 10 108 105 110 101 32 35 50 10 108 105 110 101 32 35 51 10 108 105 110 101 32 35 52 10 108 105 110 101 32 35 53 10
V Go namísto toho můžeme použít čtení po celých blocích, ovšem velikost bloku omezíme na jediný bajt:
package main import ( "fmt" "io" "log" "os" ) const filename = "test_input.txt" func main() { fin, err := os.Open(filename) if err != nil { log.Fatal(err) } defer fin.Close() buffer := make([]byte, 1) for { read, err := fin.Read(buffer) if read > 0 { fmt.Printf("byte from file\n") fmt.Println(buffer[0]) } else { fmt.Println("empty block (end of file?)") } if err == io.EOF { fmt.Println("reached end of file") break } if err != nil { fmt.Printf("other error %v\n", err) break } } }
Výsledky:
byte from file 108 byte from file 105 ... ... ... byte from file 10 empty block (end of file?) reached end of file
17. Čtení či zápis po blocích
V předchozí kapitole jsme si vlastně ukázali i způsob načítání dat ze souborů po blocích. V céčku se může jednat například o toto řešení (blok má kapacitu šestnácti bajtů):
#include <stdio.h> const char* filename = "test_input.txt"; const int BLOCK_SIZE = 16; int main(void) { FILE *fin = fopen(filename, "r"); if (!fin) { perror("Can not open file"); return 1; } char buffer[BLOCK_SIZE]; while (1) { size_t read = fread(buffer, sizeof(char), BLOCK_SIZE, fin); if (read > 0) { int i; printf("read %ld bytes\n", read); for (i=0; i<read; i++) { printf("%d ", buffer[i]); } putchar('\n'); } else { puts("reached end of file"); break; } } fclose(fin); return 0; }
Výsledky čtení dat po blocích o kapacitě šestnácti bajtů:
read 16 bytes 108 105 110 101 32 35 49 10 108 105 110 101 32 35 50 10 read 16 bytes 108 105 110 101 32 35 51 10 108 105 110 101 32 35 52 10 read 8 bytes 108 105 110 101 32 35 53 10 reached end of file
Naproti tomu v jazyce Go postačuje vzít příklad z předchozí kapitoly a zvýšit kapacitu bufferu:
package main import ( "fmt" "io" "log" "os" ) const filename = "test_input.txt" const buffer_size = 16 func main() { fin, err := os.Open(filename) if err != nil { log.Fatal(err) } defer fin.Close() buffer := make([]byte, buffer_size) for { read, err := fin.Read(buffer) if read > 0 { fmt.Printf("read %d bytes\n", read) fmt.Println(buffer[:read]) } if err == io.EOF { fmt.Println("reached end of file") break } if err != nil { fmt.Printf("other error %v\n", err) break } } }
Výsledky jsou prakticky totožné s céčkovým příkladem (pouze prvky pole se vypisují v hranatých závorkách):
read 16 bytes [108 105 110 101 32 35 49 10 108 105 110 101 32 35 50 10] read 16 bytes [108 105 110 101 32 35 51 10 108 105 110 101 32 35 52 10] read 8 bytes [108 105 110 101 32 35 53 10] reached end of file
18. Zpracování chyb, reakce na chyby
Jak klasické céčko, tak i programovací jazyk Go pracují s chybovými stavy podobným způsobem, alespoň na první pohled. Informace o chybě je vrácena volající funkci, která musí vzniklou situaci nějakým způsobem vyřešit. Ovšem v céčku právě zde narážíme na omezení programovacího jazyka, protože z funkce lze vrátit pouze jedinou hodnotu. Tato situace se řeší různými způsoby, které však jen málokdy bývají zcela vyhovující. Například je možné vracet obvyklou hodnotu (řekněme znak načtený ze standardního vstupu) popř. speciální hodnotu při chybě. Nebo se při chybě nastaví globální proměnná errno (což ovšem přináší další problémy – co se má stát při výskytu více chyb, jak se s proměnnou pracuje ve více vláknech atd. atd.). Popř. funkce vrací jen informaci o chybě a skutečné hodnoty se vrací odkazem přes nějaký parametr.
V jazyce Go je situace mnohem lepší, protože z funkce lze vrátit libovolný počet hodnot a existuje úzus, že poslední hodnotou bývá právě informace o případné chybě, která ve volané funkci nastala. Nejedná se přitom jen o rozlišení „chyba nastala“ versus „chyba nenastala“, protože samotná chyba je představována plnohodnotným objektem, který může nést i další užitečné informace – vznik chyby, chybovou zprávu, chybový kód atd. Typický příklad reakce na chybu by se v jazyce Go napsal způsobem, který jsme již viděli v předchozích dvou kapitolách:
fin, err := os.Open(filename) if err != nil { log.Fatal(err) }
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitý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á přibližně tři megabajty), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Go is on a Trajectory to Become the Next Enterprise Programming Language
https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 10 tools written in Go that every developer needs to know
https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 5 simple tips and tricks for writing unit tests in #golang
https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - 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 - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - 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 - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go