Jazyk Go pro úplné začátečníky

Presentations

Jazyk Go pro úplné začátečníky

Obsah přednášky

Last minute info

Charakteristické rysy jazyka Go

„Less is more“

Charakteristické rysy jazyka Go

Charakteristické rysy jazyka Go (2)

Charakteristické rysy jazyka Go (3)

Charakteristické rysy jazyka Go (4)

Klíčová slova jazyka Go

break     default      func    interface  select
case      defer        go      map        struct
chan      else         goto    package    switch
const     fallthrough  if      range      type
continue  for          import  return     var

Základní datové typy

Pravdivostní typ

Celočíselné datové typy

Označení          Od                   Do           Stručný popis
int8                     -128                  127  osmibitové celé číslo se znaménkem
int16                  -32768                32767  16bitové celé číslo se znaménkem
int32             -2147483648           2147483647  32bitové celé číslo se znaménkem
int64    -9223372036854775808  9223372036854775807  64bitové celé číslo se znaménkem

uint8                       0                  255  osmibitové celé číslo bez znaménka
uint16                      0                65535  16bitové celé číslo bez znaménka
uint32                      0           4294967295  32bitové celé číslo bez znaménka
uint64                      0 18446744073709551615  64bitové celé číslo bez znaménka

int                     různý                různý  odpovídá buď typu int32 nebo int64
uint                    různý                různý  odpovídá buď typu uint32 nebo uint64

byte                        0                  255  alias pro typ uint8
rune              -2147483648           2147483647  alias pro typ int32

Celočíselné datové typy

        var a int8 = -10
        var b int16 = -1000
        var c int32 = -10000
        var d int32 = -1000000

        var r1 rune = 'a'
        var r2 rune = '\x40'
        var r3 rune = '\n'
        var r4 rune = '\u03BB'

        var x uint8 = 10
        var y uint8 = 010
        var z uint8 = 0x10

Explicitní konverze!

        var a int8 = -10
        var signed_int int32 = -100000
        var unsigned_int uint32 = 100000
        var e float32 = 1e4
        var f float64 = 1.5e30

        var x int32 = int32(a)
        var y int32 = int32(e)
        var z float32 = float32(f)

        var b2 uint8 = uint8(signed_int)
        var b3 uint8 = uint8(unsigned_int)

Neordinální celočíselné datové typy

Označení        Rozsah hodnot                          Stručný popis
float32         -3,4×10³⁸ až 3,4×10³⁸                  číslo s jednoduchou přesností podle IEEE 754
float64         -1,7×10³⁰⁸ až 1,7×10³⁰⁸                číslo s dvojitou přesností podle IEEE 754
complex64       ± rozsah float32 + i ± rozsah float32  dvojice hodnot s jednoduchou přesností
complex128      ± rozsah float64 + i ± rozsah float64  dvojice hodnot s dvojitou přesností

Neordinální celočíselné datové typy

        var a float32 = -1.5
        var b float32 = 1.5
        var c float32 = 1e30
        var d float32 = 1e-30

        var x complex64 = -1.5 + 0i
        var y complex64 = 1.5 + 1000i
        var z complex64 = 1e30 + 1e30i
        var w complex64 = 1i

Řetězce (string)

        fmt.Println("╭─────────────────────╮")
        fmt.Println("│ příλiš žλuťΩučký kůň│")
        fmt.Println("╰─────────────────────╯")

Pole bajtů tvořících řetězec

        var s string = "Hello\nworld!\nžluťoučký kůň"

        for i := 0; i < len(s); i++ {
                fmt.Printf("%02x ", s[i])
        }

Pole (array)

        var a1 [10]byte
        a2 := [10]int32{1,10,2,9,3,8,4,7,5,6}
        var matice [10][10]float32
 
        for i:= 0; i < len(a1); i++ {
                a[i] = i*2;
        }

Kopie polí

        a2 := a1

Řezy (slice)

Řezy (slice)

        a := [6]string{"C", "C++", "Java", "Python", "Go", "Rust"}
  
        slice1 := a[1:4]
        slice2 := a[:3]
        slice3 := a[2:]
        slice4 := a[:]
  
        fmt.Println("Array a =", a)
        fmt.Println("slice1 =", slice1)
        fmt.Println("slice2 =", slice2)
        fmt.Println("slice3 =", slice3)
        fmt.Println("slice4 =", slice4)

Řezy (slice)

Array a = [C C++ Java Python Go Rust]
slice1 = [C++ Java Python]
slice2 = [C C++ Java]
slice3 = [Java Python Go Rust]
slice4 = [C C++ Java Python Go Rust]

Uživatelsky definované datové typy

package main
  
import "fmt"
  
type Id uint32
type Name string
type Surname string
  
func register_user(id Id, name Name, surname Surname) {
        fmt.Printf("Registering: %d %s %s", id, name, surname)
}
  
func main() {
        var i Id = 1
        var n Name = "Jan"
        var s Surname = "Novák"
   
        register_user(i, n, s)
}

Operátor :=

        a := 10
        fmt.Println(a)
        b := "hello"
        fmt.Println(b)
        c := true
        fmt.Println(c)

Operátor :=

Záznamy (struct)

Záznamy (struct)

type User struct {
        id      uint32
        name    string
        surname string
}
   
var user1 User
   
user1.id = 1
user1.name = "Pepek"
user1.surname = "Vyskoč"

Tisk obsahu záznamů a inicializace záznamů

fmt.Println(user1)
user1 := User{
        1,
        "Pepek",
        "Vyskoč"}

Pole záznamů

type User struct {
        id      uint32
        name    string
        surname string
}
   
var users = [3]User{
        User{
                id:      1,
                name:    "Pepek",
                surname: "Vyskoč"},
        User{
                id:      2,
                name:    "Pepek",
                surname: "Vyskoč"},
        User{
                id:      3,
                name:    "Josef",
                surname: "Vyskočil"},
}

Mapy (map)

Operace s mapami

if exist {
        // prvek byl nalezen
} else {
        // prvek nebyl nalezen
}

Mapy a struktury (záznamy)

type User struct {
        id      uint32
        name    string
        surname string
}
   
func main() {
        m1 := make(map[string]User)
        fmt.Println(m1)
  
        m1["prvni"] = User{
                id:      1,
                name:    "Pepek",
                surname: "Vyskoč"}
  
        m1["druhy"] = User{
                id:      2,
                name:    "Josef",
                surname: "Vyskočil"}
  
        fmt.Println(m1)
}

Mapy a struktury, klíče jako uživatelský typ

type Key struct {
        id   uint32
        role string
}
   
type User struct {
        id      uint32
        name    string
        surname string
}
   
func main() {
        m1 := make(map[Key]User)
        fmt.Println(m1)
   
        m1[Key{1, "admin"}] = User{
                id:      1,
                name:    "Pepek",
                surname: "Vyskoč"}
   
        m1[Key{2, "user"}] = User{
                id:      2,
                name:    "Josef",
                surname: "Vyskočil"}
   
        fmt.Println(m1)
}

Zvláštní datové typy

Ukazatele

var p_i *int
p_i = &i
*p_i++

Ukazatel na strukturu

var u User
   
var p_u *User
p_u = &u

Konstanty

const Pi float64 = 3.1415927 const E = 2.71828

const z0 int = 0 const z1 = 0

const z2 = z0 + z1

Funkce

var a func(int) int

a = funkce1

fmt.Println(a) fmt.Println(a(10))

type two_int_param_function func(int, int) int var b two_int_param_function

Deklarace funkcí

Staticky typovaný jazyk bez zbytečného “boilerplate”

func swap(a int, b int) (int, int) { return b, a }

    x := 1
    y := 2
    z, w := swap(x, y)

Operátory

aritmetické + - * / % aritmetické s přiřazením += -= *= /= %= logické && || ! posuny a bitové operace «  » & | ^ &^ -//- s přiřazením «= »= &= |= ^= &^= relační == != < <= > >= operace s adresami * & unární operátory + - ^ další operátory <- :=

Operátory - výběr zajímavostí

^ negace bit po bitu (podobně jako operátor ~ v C)

& &= logický součin prováděný bit po bitu | |= logický součet prováděný bit po bitu ^ ^= logická nonekvivalence prováděná bit po bitu &^ &^= maskování bitů vybraných zadanou maskou (operace AND NOT)

Operátory/příkazy ++ a –

Řídicí příkazy

Příkaz return

// kontrola existence return hodnota i u nedosažitelného kódu func f2() int { println(“f2() před příkazem return”) return 42 println(“f2() po příkazu return”) return -1 }

Rozhodovací konstrukce if

func classify_char(c rune) string { if c >= ‘a’ && c <= ‘z’ { return “male pismeno” } else if c >= ‘A’ && c <= ‘Z’ { return “velke pismeno” } else { return “neco jineho” } }

Příkaz zapsaný za klíčovým slovem if

func x() string { if value := funkce(); value < 0 { return “záporná hodnota” } else if value > 0 { return “kladná hodnota” } else { return “nula” } }

Rozvětvení běhu programu s využitím konstrukce switch

switch { }

switch { default: println(“proč jsem vlastně použil switch?”) }

switch { case true: println(“true”) case false: println(“false”) }

switch { case false: println(“false”) case true: println(“true”) default: println(“default”) }

switch { case false: println(“false”) default: println(“default”) case true: println(“true”) }

Porovnání výrazu s konstantami v konstrukci switch

func classify(x int) string { switch x { case 0: return “nula” case 2, 4, 6, 8: return “sudé číslo” case 1, 3, 5, 7, 9: return “liché číslo” default: return “?” } }

Porovnání výrazu s vypočtenými hodnotami v konstrukci switch

func classify(x int, zero_value int) string { switch x { case zero_value: return “nula” case 2, 4, 6, 8: return “sudé číslo” case 1, 3, 5, 7, 9: return “liché číslo” default: return “?” } }

Vyhodnocení a porovnání výsledků podmínek zapsaných ve větvích case

func classify(x int) string { switch { case x == 0: return “nula” case x%2 == 0: return “sudé číslo” case x%2 == 1: return “liché číslo” default: return “?” } }

Klíčové slovo fallthrough

// Pozor!!! func classify(x int) string { switch x { case 0: return “nula” case 2: case 4: case 6: case 8: return “sudé číslo” case 1: case 3: case 5: case 7: case 9: return “liché číslo” default: return “?” } return “X” }

Klíčové slovo fallthrough

func classify(x int) string { switch x { case 0: return “nula” case 2: fallthrough case 4: fallthrough case 6: fallthrough case 8: return “sudé číslo” case 1: fallthrough case 3: fallthrough case 5: fallthrough case 7: fallthrough case 9: return “liché číslo” default: return “?” } }

Programové smyčky - klasická nekonečná smyčka

for { println(“Diamonds are forever”) }

Programové smyčky - podmínka uprostřed smyčky

for { … … … if podmínka { break } … … … }

Programové smyčky - podmínka na začátku smyčky

for i != 0 { println(i) i– }

Programové smyčky - počítaná smyčka

for i := 0; i < 10; i++ { println(i) }

Programové smyčky - iterace přes datové struktury

a := […]int{1, 2, 10, -1, 42}

for index, item := range a { println(index, item) }

println()

s := “Hello world ěščř Σ”

for index, character := range s { println(index, character) }

for _, item := range a { println(item) }

Programové smyčky a mapy

var m1 map[int]string = make(map[int]string) m1[0] = “nula” m1[1] = “jedna” m1[2] = “dva” m1[3] = “tri” m1[4] = “ctyri” m1[5] = “pet” m1[6] = “sest”

for key, val := range m1 { println(key, val) }

Break a continue (s návěštím)

Exit: for i := 1; i <= 10; i++ { for j := 1; j <= 10; j++ { fmt.Printf(“%3d “, ij) if ij == 42 { fmt.Println(“\nodpověď nalezena!\n”) break Exit } } fmt.Println() }

Příkaz goto

func classify(x int) string { switch x { case 0: return “nula” case 2, 4, 6, 8: goto SudeCislo case 1, 3, 5, 7, 9: goto LicheCislo default: goto JineCislo } JineCislo: return “?” SudeCislo: return “sudé číslo” LicheCislo: return “liché číslo” }

Používá se goto vůbec?

Počet použití Klíčové slovo 189 fallthrough 550 goto 605 select 1711 chan 2346 interface 2412 default 3128 map 3443 continue 3831 import 3946 defer 4433 switch 4709 go 4929 const 5308 package 7125 else 9418 range 9885 struct 12313 type 19094 var 22073 case 29449 break 29736 for 57261 func 77351 return 111163 if

Klíčové slovo defer

Speciální řízení (defer)

defer on_finish()

for i := 10; i >= 0; i– { fmt.Printf(“%2d\n”, i) } fmt.Println(“Finishing main() function”)

Speciální řízení (defer)

src, err := os.Open(srcName) if err != nil { fmt.Printf(“Cannot open file ‘%s’ for reading\n”, srcName) return } else { fmt.Printf(“File ‘%s’ opened for reading\n”, srcName) } defer closeFile(src) // nebo přímo defer src.Close()

Ovlivnění návratové hodnoty přes blok defer

func funkce1() (i int) { i = 1 return }

func funkce2() (i int) { defer func() { i = 2 }() return 1 }

func funkce3() (i int) { defer func() { i += 2 }() return 1 }

Reakce na chybové stavy

type error interface { Error() string }

func div(x, y int32) (int32, error) { if y == 0 { return -1, errors.New(“takto ne!”) } return x / y, nil }

func main() { res, err := div(10, 3) fmt.Println(res, err) res, err = div(10, 0) fmt.Println(res, err) }

Reakce na chybové stavy - panic

Reakce na chybové stavy - panic

func copyFile(srcName, dstName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { panic(err) } defer closeFile(src)

    dst, err := os.Create(dstName)
    if err != nil {
            panic(err)
    }
    defer closeFile(dst)
 
    return io.Copy(dst, src) }

Rozhraní

Metody

func (line Line) length() float64 { … … … }

line1 := Line{x1: 0, y1: 0, x2: 100, y2: 100} line_length := line1.length()

Metody měnící příjemce

Rozhraní jako datový typ při volání funkcí

type OpenShape interface { length() float64 }

func length(shape OpenShape) float64 { return shape.length() }

type Line struct { x1, y1 float64 x2, y2 float64 }

func (line Line) length() float64 { return math.Hypot(line.x1-line.x2, line.y1-line.y2) }

line1 := Line{x1: 0, y1: 0, x2: 100, y2: 100}

fmt.Println(line1)

line_length := length(line1) fmt.Println(line_length)

Složitější příklad

package main

import ( “fmt” “math” )

type ClosedShape interface { area() float64 }

func area(shape ClosedShape) float64 { return shape.area() }

type Circle struct { x, y float64 radius float64 }

type Ellipse struct { x, y float64 a, b float64 }

type Rectangle struct { x, y float64 width, height float64 }

func (rect Rectangle) area() float64 { return rect.width * rect.height }

func (circle Circle) area() float64 { return math.Pi * circle.radius * circle.radius }

func (ellipse Ellipse) area() float64 { return math.Pi * ellipse.a * ellipse.b }

func main() { shapes := []ClosedShape{ Rectangle{x: 0, y: 0, width: 100, height: 100}, Circle{x: 0, y: 0, radius: 100}, Ellipse{x: 0, y: 0, a: 100, b: 50}}

    for _, shape := range shapes {
            fmt.Println(shape)
            fmt.Println(area(shape))
            fmt.Println(shape.area())
            fmt.Println()
    } }

Gorutiny

func f(from string) { for i := 0; i < 3; i++ { fmt.Println(from, “:”, i) } }

func main() { f(“direct”) go f(“goroutine”) go func(msg string) { fmt.Println(msg) }(“going”)

fmt.Scanln()
fmt.Println("done") }

func main() { messages := make(chan string) go func() { messages <- “ping” }() msg := <-messages fmt.Println(msg) }

Kanály

Kanály

func message(id int, channel chan int) { fmt.Printf(“gorutina %d\n”, id)

    // zápis libovolné hodnoty do kanálu
    channel <- 1 }

func main() { channel := make(chan int)

    fmt.Println("main begin")
    go message(1, channel)
   
    fmt.Println("waiting...")
   
    // blokující čtení z kanálu
    code, status := <-channel
   
    fmt.Printf("received code: %d and status: %t\n", code, status)
    fmt.Println("main end") }

Jednoduchá synchronizace v Go

func worker(done chan bool) { fmt.Print(“working…”) time.Sleep(time.Second) fmt.Println(“done”)

// ok uz jsme hotovi, posleme zpravu kanalem
done <- true }

func main() { // kanal s kapacitou == 1 done := make(chan bool, 1)

// asynchronni beh
go worker(done)
  
// cekame na zpravu
<-done }

Čekání na data posílaná přes kanály

ch1 := make(chan int) ch2 := make(chan int)

go worker(ch1, 1) go worker(ch2, 2)

select { case <-ch1: fmt.Println(“Data z kanálu 1”) case <-ch2: fmt.Println(“Data z kanálu 2”) }

Větev default

ch1 := make(chan int) ch2 := make(chan int)

go worker(ch1, 1) go worker(ch2, 2)

for true { select { case <-ch1: fmt.Println(“Data z kanálu 1”) case <-ch2: fmt.Println(“Data z kanálu 2”) default: fmt.Println(“Žádná data nejsou k dispozici”) } time.Sleep(1 * time.Second) }

Posílání dat do workerů

ch1 := make(chan int)

go worker(ch1)

for i := 0; i < 10; i++ { select { case ch1 <- 0: fmt.Println(“Poslána nula”) case ch1 <- 1: fmt.Println(“Poslána jednička”) } }

Blokující zápis do kanálu

func worker(channel chan int, worker int) { for true { value, ok := <-channel if ok { fmt.Printf(“Worker %d přijal hodnotu %d\n”, worker, value) } else { fmt.Printf(“Kanál je uzavřen pro workera %d\n”, worker) } time.Sleep(1 * time.Second) } }

func main() { ch1 := make(chan int)

    go worker(ch1, 1)
    go worker(ch1, 2)
   
    for i := 0; i < 10; i++ {
            select {
            case ch1 <- 0:
                    fmt.Println("Poslána nula")
            case ch1 <- 1:
                    fmt.Println("Poslána jednička")
            }
    } }

Kombinace čtení a zápisu v konstrukci select-case

select { case ch1 <- 0: fmt.Println(“Poslána nula”) case ch1 <- 1: fmt.Println(“Poslána jednička”) case data, ok := <-ch2: if ok { fmt.Printf(“Přijata data %d z kanálu 2\n”, data) } case data, ok := <-ch3: if ok { fmt.Printf(“Přijata data %d z kanálu 3\n”, data) } }

Balíčky

Makra v Go?

A samozřejmě oblíbené téma pro debaty…