Hlavní navigace

Programovací jazyk Go pro skalní céčkaře (2.část)

Pavel Tišnovský

V dnešní části seriálu o jazyce Go si ukážeme další vlastnosti Go, které mohou překvapit ty vývojáře, kteří primárně používají programovací jazyk C a v menší míře i C++. Ukážeme si odlišnou práci s řetězci, poli, soubory a taktéž s chybami (chybovými stavy aplikace).

Doba čtení: 45 minut

Sdílet

11. Vytvoření polí

12. Kopie polí

13. Vícerozměrná pole

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)

17. Čtení či zápis po blocích

18. Zpracování chyb, reakce na chyby

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

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
Poznámka: mohlo by se zdát, že se jedná o přílišnou rigiditu, ovšem díky těmto pravidlům se prakticky zcela odstraní chyby typu = vs. == ve výrazu za klíčovým slovem if, které většina překladačů céčka pouze ohlašuje ve formě varování:
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
Poznámka pro programátory používající jazyk Python: prázdný řetězec není v céčku považován za nepravdu, protože se interně jedná o ukazatel na řetězcový literál (zde konkrétně uložený v sekci .rodata, překladač však většinou celou podmínku z kódu odstraní). Tento příklad tedy dvakrát za sebou vypíše zprávu true:
#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;
}
Poznámka: podívejme se pro zajímavost na způsob interní reprezentace všech čtyř řetězců v assembleru. Postačuje provést překlad s uvedením volby -S a podívat se na výsledný kód vygenerovaný překladačem (za povšimnutí stojí i to, že se řetězce skutečně uložily do sekce nazvané .rodata neboli read-only data):
        .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 3­21 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 я
Poznámka: ve skutečnosti mohou mít znaky (characters) reprezentované v UTF-8 proměnnou délku od jednoho bajtu do čtyř bajtů (teoreticky, pokud se Unicode někdy v budoucnosti rozšíří z 21 bitů na plných 32 bitů, se může jednat i o více bajtů).

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;
}
Poznámka: samotné naplnění pole se překladače většinou snaží optimalizovat, takže se neprovádí inicializace bajt po bajtu (například kopií z kódového segmentu), ale inicializace po větších celcích. Konkrétně na 32bitové platformě x386 může inicializace pole vypadat následovně (vždy po čtyřech bajtech, pochopitelně včetně koncové nuly):
    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:

[шщэюя]
*шщ-�юя*
Poznámka: pokud jsme změnili první bajt znaku, dojde k poškození řetězce ve dvou znacích, protože ‚-‘ reprezentuje hodnotu menší než 128, což mj. znamená, že je tímto bajtem znak ukončen. A následující hodnota je již považována za znak nový.

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í:

[шщэюя]
*шщ-юя*
Poznámka: pole run je nutné převést zpět na řetězec s využitím string(), protože jinak by se vypsaly numerické kódy (indexy) znaků a nikoli jejich tvary.

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")
}
Poznámka: v praxi to znamená, že se používají operátory == a !=, tj. test na rovnost nebo naopak nerovnost dvou řetězců, ale pro lexikografické porovnání je lepší použít přístup popsaný v navazující kapitole.

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"))
Poznámka: pravděpodobně mnohem užitečnější je v balíčku collate funkce pro seřazení řetězců. Tato funkce opět pracuje korektně pro nastavený jazyk.

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;
}
Poznámka: v předchozím příkladu jsem schválně použil výraz s1 + 0, protože onu zbytečnou nulu lze nahradit indexem ASCII znaku.

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í:

эю
эюя
эюя
 
ю
юя
юя
Poznámka: tento způsob se nedoporučuje používat v těch případech, kdy se jedná o dlouhé řetězce popř. o často prováděnou operaci, protože převod řetězce na pole run (a případný zpětný převod na řetězec) může být časově náročný.
Poznámka2: nutno říci, že v některých jazycích je situace okolo řetězců (a Unicode) ještě složitější. Například v Javě je nutné dbát na to, že znaky asijských abeced jsou uloženy ve formě páru (surrogate pairs), takže některé metody (String.charAt() atd.) nemusí vracet očekávatelné výsledky.

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
Poznámka: to znamená, že ne všechny algoritmy je možné snadno a bez přemýšlení přepsat z C do Go.

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
Poznámka: můžeme vidět, že v současnosti používaný algoritmus vždy zdvojnásobí kapacitu pole, které slouží jako datová základna pro řez.

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)
}
Poznámka: pokud funkce vrací více hodnot a současně i informaci o chybě, je vhodné, aby informace o chybě byla poslední návratovou hodnotou funkce.

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:

# Příklad Cesta
1 01_booleans.c https://github.com/tisnik/go-root/blob/master/article35/01_bo­oleans.c
1 01_booleans.go https://github.com/tisnik/go-root/blob/master/article35/01_bo­oleans.go
     
2 02_boolean_type_checks.go https://github.com/tisnik/go-root/blob/master/article35/02_bo­olean_type_checks.go
     
3 03_strings.c https://github.com/tisnik/go-root/blob/master/article35/03_strin­gs.c
3 03_strings.go https://github.com/tisnik/go-root/blob/master/article35/03_strin­gs.go
3 03_strings_B.go https://github.com/tisnik/go-root/blob/master/article35/03_strin­gs_B.go
3 03_strings.s https://github.com/tisnik/go-root/blob/master/article35/03_strin­gs.s
     
4 04_immutable_strings.c https://github.com/tisnik/go-root/blob/master/article35/04_im­mutable_strings.c
4 04_immutable_strings.go https://github.com/tisnik/go-root/blob/master/article35/04_im­mutable_strings.go
     
5 05_mutable_strings.c https://github.com/tisnik/go-root/blob/master/article35/05_mu­table_strings.c
5 05_mutable_strings.go https://github.com/tisnik/go-root/blob/master/article35/05_mu­table_strings.go
     
6 06_mutable_utf8_strings.c https://github.com/tisnik/go-root/blob/master/article35/06_mu­table_utf8_strings.c
6 06_mutable_utf8_strings.go https://github.com/tisnik/go-root/blob/master/article35/06_mu­table_utf8_strings.go
6 06_mutable_utf8_strings_B.go https://github.com/tisnik/go-root/blob/master/article35/06_mu­table_utf8_strings_B.go
     
7 07_string_comparison.c https://github.com/tisnik/go-root/blob/master/article35/07_strin­g_comparison.c
7 07_string_comparison.go https://github.com/tisnik/go-root/blob/master/article35/07_strin­g_comparison.go
7 07_string_comparison_B.go https://github.com/tisnik/go-root/blob/master/article35/07_strin­g_comparison_B.go
7 07_string_comparison_C.go https://github.com/tisnik/go-root/blob/master/article35/07_strin­g_comparison_C.go
     
8 08_string_concatenation.c https://github.com/tisnik/go-root/blob/master/article35/08_strin­g_concatenation.c
8 08_string_concatenation.go https://github.com/tisnik/go-root/blob/master/article35/08_strin­g_concatenation.go
     
9 09_substring.c https://github.com/tisnik/go-root/blob/master/article35/09_sub­string.c
9 09_substring.go https://github.com/tisnik/go-root/blob/master/article35/09_sub­string.go
9 09_substring_B.go https://github.com/tisnik/go-root/blob/master/article35/09_sub­string_B.go
9 09_substring_C.go https://github.com/tisnik/go-root/blob/master/article35/09_sub­string_C.go
     
10 10_arrays.c https://github.com/tisnik/go-root/blob/master/article35/10_a­rrays.c
10 10_arrays_B.c https://github.com/tisnik/go-root/blob/master/article35/10_a­rrays_B.c
10 10_arrays_C.c https://github.com/tisnik/go-root/blob/master/article35/10_a­rrays_C.c
10 10_arrays.go https://github.com/tisnik/go-root/blob/master/article35/10_a­rrays.go
10 10_arrays_C.go https://github.com/tisnik/go-root/blob/master/article35/10_a­rrays_C.go
     
11 11_array_copy.go https://github.com/tisnik/go-root/blob/master/article35/11_a­rray_copy.go
     
12 12_read_byte.c https://github.com/tisnik/go-root/blob/master/article35/12_re­ad_byte.c
12 12_io_reader_read_byte.go https://github.com/tisnik/go-root/blob/master/article35/12_i­o_reader_read_byte.go
     
13 13_read_ascii_char.c https://github.com/tisnik/go-root/blob/master/article35/13_re­ad_ascii_char.c
13 13_io_reader_read_ascii_char.go https://github.com/tisnik/go-root/blob/master/article35/13_i­o_reader_read_ascii_char.go
     
14 14_read_block.c https://github.com/tisnik/go-root/blob/master/article35/14_re­ad_block.c
14 14_io_reader_read_block.go https://github.com/tisnik/go-root/blob/master/article35/14_i­o_reader_read_block.go

20. Odkazy na Internetu

  1. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  2. Sémantické verzování
    https://semver.org/
  3. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  4. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  5. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  6. Modules
    https://github.com/golang/go/wi­ki/Modules
  7. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  8. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  9. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  10. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  11. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  12. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  13. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  14. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  15. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  16. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  17. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  18. gopls
    https://github.com/golang/go/wi­ki/gopls
  19. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  20. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  21. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  22. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  23. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  24. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  25. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  26. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  27. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  28. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  29. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  30. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  31. Package trace
    https://golang.org/pkg/runtime/trace/
  32. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  33. Command trace
    https://golang.org/cmd/trace/
  34. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  35. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  36. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  37. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  38. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  39. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  40. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  41. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  42. 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/
  43. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  44. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  45. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  46. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  47. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  48. The LLDB Debugger
    http://lldb.llvm.org/
  49. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  50. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  51. 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
  52. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  53. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  54. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  55. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  56. 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/
  57. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  58. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  59. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  60. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  61. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  62. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  63. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  64. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  65. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  66. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  67. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  68. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  69. go-cron
    https://github.com/rk/go-cron
  70. gocron
    https://github.com/jasonlvhit/gocron
  71. clockwork
    https://github.com/whiteShtef/cloc­kwork
  72. clockwerk
    https://github.com/onatm/clockwerk
  73. JobRunner
    https://github.com/bamzi/jobrunner
  74. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  75. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  76. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  77. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  78. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  79. go-prompt
    https://github.com/c-bata/go-prompt
  80. readline
    https://github.com/chzyer/readline
  81. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  82. go-readline
    https://github.com/fiorix/go-readline
  83. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  84. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  85. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  86. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  87. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  88. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  89. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  90. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  91. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  92. Editline Library (libedit)
    http://thrysoee.dk/editline/
  93. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  94. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  95. WinEditLine
    http://mingweditline.sourceforge.net/
  96. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  97. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  98. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  99. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  100. history(3) – Linux man page
    https://linux.die.net/man/3/history
  101. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  102. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  103. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  104. Balíček ogletest
    https://github.com/jacobsa/ogletest
  105. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  106. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  107. package testing
    https://golang.org/pkg/testing/
  108. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  109. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  110. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  111. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  112. GoConvey
    http://goconvey.co/
  113. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  114. 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
  115. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  116. package gg
    https://godoc.org/github.com/fo­gleman/gg
  117. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  118. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  119. The Go image package
    https://blog.golang.org/go-image-package
  120. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  121. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  122. YAML
    https://yaml.org/
  123. edn
    https://github.com/edn-format/edn
  124. Smile
    https://github.com/FasterXML/smile-format-specification
  125. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  126. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  127. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  128. Introducing JSON
    http://json.org/
  129. Package json
    https://golang.org/pkg/encoding/json/
  130. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  131. Go by Example: JSON
    https://gobyexample.com/json
  132. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  133. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  134. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  135. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  136. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  137. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  138. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  139. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  140. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  141. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  142. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  143. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  144. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  145. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  146. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  147. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  148. Algorithms to Go
    https://yourbasic.org/
  149. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  150. 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/
  151. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  152. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  153. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  154. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  155. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  156. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  157. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  158. The Go Programming Language (home page)
    https://golang.org/
  159. GoDoc
    https://godoc.org/
  160. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  161. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  162. The Go Programming Language Specification
    https://golang.org/ref/spec
  163. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  164. Package builtin
    https://golang.org/pkg/builtin/
  165. Package fmt
    https://golang.org/pkg/fmt/
  166. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  167. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  168. Learning Go
    https://www.miek.nl/go/
  169. Go Bootcamp
    http://www.golangbootcamp.com/
  170. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  171. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  172. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  173. The Go Blog
    https://blog.golang.org/
  174. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  175. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  176. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  177. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  178. 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
  179. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  180. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  181. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  182. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  183. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  184. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  185. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  186. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  187. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  188. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  189. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  190. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  191. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  192. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  193. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  194. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  195. 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/
  196. 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
  197. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  198. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  199. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  200. Go vs. Python
    https://www.peterbe.com/plog/govspy
  201. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  202. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  203. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  204. Go by Example: Slices
    https://gobyexample.com/slices
  205. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  206. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  207. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  208. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  209. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  210. nils In Go
    https://go101.org/article/nil.html
  211. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  212. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  213. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  214. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  215. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  216. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  217. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  218. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  219. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  220. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  221. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  222. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  223. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  224. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  225. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  226. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  227. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  228. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  229. 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
  230. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  231. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  232. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  233. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  234. Selectors
    https://golang.org/ref/spec#Selectors
  235. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  236. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  237. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  238. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  239. Part 21: Goroutines
    https://golangbot.com/goroutines/
  240. Part 22: Channels
    https://golangbot.com/channels/
  241. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  242. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  243. 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/
  244. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  245. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  246. Control Structures
    https://www.golang-book.com/books/intro/5
  247. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  248. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  249. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  250. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  251. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  252. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  253. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  254. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  255. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  256. Effective Go
    https://golang.org/doc/ef­fective_go.html
  257. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  258. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  259. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  260. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  261. Nils in Go
    https://www.doxsey.net/blog/nils-in-go