📜 ⬆️ ⬇️

50 shades of Go: traps, pitfalls and common mistakes beginners



Go is a simple and fun language. But in it, as well as in any other languages, there are reefs. And in many of them Go itself is not to blame. Some are a natural consequence of the arrival of programmers from other languages, others arise from misconceptions and lack of details. If you find time and read the official specifications, wikis, mailing lists, blog posts and source code, many of the pitfalls will become obvious to you. But not everybody starts like that, and that's fine. If you are new to Go, the article will help you save a lot of hours that you would spend on debugging code. We will consider versions of Go 1.5 and below.

Content


Level: absolute beginner

1. The opening brace cannot be placed on a separate line.
2. Unused variables
3. Unused imports
4. Short variable declarations can only be used inside functions.
5. Redeclaration of Variables with Short Declarations
6. You cannot use short variable declarations to set field values.
7. Random variable hiding
8. You cannot use nil to initialize a variable without explicitly specifying the type.
9. Using nil slices (slice) and hash tables (map)
10. Capacity of hash tables
11. Strings cannot be nil
12. Passing arrays to functions
13. Unexpected values ​​in range expressions in slices and arrays
14. One-dimensionality of slices and arrays
15. Accessing non-existent keys in the map
16. The immutability of the lines
17. String conversion to byte slices (Byte Slices), and vice versa
18. Strings and operator index
19. Strings - not always text encoded in UTF-8
20. Line length
21. Missing comma in multiline slice / array / map literals
22. log.Fatal and log.Panic not only log
23. Unsynchronized operations of embedded data structures
24. Iterative values ​​for rows in range expressions
25. Iteration of hash tables (map) using the expression for range
26. Failure behavior in switch expressions
27. Increments and decrements
28. Bitwise NOT-operator
29. Differences in operator priorities
30. Unexported structure fields are not encoded.
31. Exit applications using active gorutin
32. When sent to an unbuffered channel, data is returned as the recipient is ready.
33. Sending to a closed channel leads to panic
34. Using "nil" channels
35. Methods that take parameters by value do not change the original values.
')
Level: more experienced beginner

36. Closing HTTP response body
37. Close HTTP connections
38. Deserialization (unmarshalling) JSON numbers into interface values
39. Comparison of struct, array, slice and map
40. Recovery after panic
41. Updating and binding field values ​​in slice, array and map in expressions for range
42. "Hidden data" in slices
43. "Damage" data in slices
44. "Outdated" slices
45. Methods and type declarations.
46. How to get out of the code blocks for switch and for select
47. Iterative variables and closures in for expressions
48. Calculate the argument block defer (Deferred Function Call Argument Evaluation)
49. Call block defer
50. Errors in casting
51. Blocked gorutiny and resource leaks

Level: advanced beginner

52. Applying methods that take value by reference (pointer receiver) to value instances
53. Updating value fields in a hash table
54. nil interfaces and nil interface values
55. Stack and heap variables
56. GOMAXPROCS, consistency (concurrency) and concurrency
57. Changing the order of read and write operations.
58. Preemptive Scheduling

1. The opening brace cannot be placed on a separate line.


In most other languages ​​that use curly braces, you need to choose where to place them. Go out of the rule. For this, you can thank the automatic insertion of a semicolon (semicolon is assumed at the end of each line, without analyzing the next one). Yes, there is a semicolon in Go!

Wrong:

package main

import "fmt"

func main()  
{ // ,        
    fmt.Println("hello there!")
}

:

/tmp/sandbox826898458/main.go:6: syntax error: unexpected semicolon or newline before {

:

package main

import "fmt"

func main() {  
    fmt.Println("works!")
}

2.


, . : , . . .

, . - , .

:

package main

var gvar int // not an error

func main() {  
    var one int   // ,  
    two := 2      // ,  
    var three int // ,      3   
    three = 3

    func(unused string) {
        fmt.Println("Unused arg. No compile error")
    }("what?")
}

:

/tmp/sandbox473116179/main.go:6: one declared and not used /tmp/sandbox473116179/main.go:7: two declared and not used /tmp/sandbox473116179/main.go:8: three declared and not used

:

package main

import "fmt"

func main() {  
    var one int
    _ = one

    two := 2
    fmt.Println(two)

    var three int
    three = 3
    one = three

    var four int
    four = four
}

: .

3.


- , , , . , «_» . «_» - .

:

package main

import (  
    "fmt"
    "log"
    "time"
)

func main() {  
}

:

/tmp/sandbox627475386/main.go:4: imported and not used: "fmt" /tmp/sandbox627475386/main.go:5: imported and not used: "log" /tmp/sandbox627475386/main.go:6: imported and not used: "time"

:

package main

import (  
    _ "fmt"
    "log"
    "time"
)

var _ = log.Println

func main() {  
    _ = time.Now
}

: . goimports.

4.


:

package main

myvar := 1 // 

func main() {  
}

:

/tmp/sandbox265716165/main.go:3: non-declaration statement outside function body

:

package main

var myvar = 1

func main() {  
}

5.


, (multi-variable declarations), — . , (shadowed variable).
:

package main

func main() {  
    one := 0
    one := 1 // 
}

:

/tmp/sandbox706333626/main.go:5: no new variables on left side of :=

:

package main

func main() {  
    one := 0
    one, two := 1,2

    one,two = two,one
}

6.


:

package main

import (  
  "fmt"
)

type info struct {  
  result int
}

func work() (int,error) {  
    return 13,nil  
  }

func main() {  
  var data info

  data.result, err := work() // 
  fmt.Printf("info: %+v\n",data)
}

:

prog.go:18: non-name data.result on left side of :=

Go , : « ». . .

:

package main

import (  
  "fmt"
)

type info struct {  
  result int
}

func work() (int,error) {  
    return 13,nil  
  }

func main() {  
  var data info

  var err error
  data.result, err = work() // ok
  if err != nil {
    fmt.Println(err)
    return
  }

  fmt.Printf("info: %+v\n",data) // : info: {result:13}
}

7.


( , Go ), . , , .

package main

import "fmt"

func main() {  
    x := 1
    fmt.Println(x)     //  1
    {
        fmt.Println(x) //  1
        x := 2
        fmt.Println(x) //  2
    }
    fmt.Println(x)     //  1 (,    2)
}

Go-. . vet. . -shadow: go tool vet -shadow your_file.go

8. nil


nil « » (zero value) , , , - (map), (slices) . , , .

:

package main

func main() {  
    var x = nil // 

    _ = x
}

:

/tmp/sandbox188239583/main.go:4: use of untyped nil

:

package main

func main() {  
    var x interface{} = nil

    _ = x
}

9. nil- (slice) - (map)


nil-, -, runtime panic.

:

package main

func main() {  
    var s []int
    s = append(s,1)
}

:

package main

func main() {  
    var m map[string]int
    m["one"] = 1 // 

}

10. -


-, cap().

:

package main

func main() {  
    m := make(map[string]int,99)
    cap(m) // 
}

:

/tmp/sandbox326543983/main.go:5: invalid argument m (type map[string]int) for cap

11. nil


, nil-.

:

package main

func main() {  
    var x string = nil // 

    if x == nil { // 
        x = "default"
    }
}

:

/tmp/sandbox630560459/main.go:4: cannot use nil as type string in assignment /tmp/sandbox630560459/main.go:6: invalid operation: x == nil (mismatched types string and nil)

:

package main

func main() {  
    var x string //     "" ( )

    if x == "" {
        x = "default"
    }
}

12.


/++, — . , . Go , , , . , .

package main

import "fmt"

func main() {  
    x := [3]int{1,2,3}

    func(arr [3]int) {
        arr[0] = 7
        fmt.Println(arr) //  [7 2 3]
    }(x)

    fmt.Println(x) //  [1 2 3] (,     [7 2 3])
}

, .

package main

import "fmt"

func main() {  
    x := [3]int{1,2,3}

    func(arr *[3]int) {
        (*arr)[0] = 7
        fmt.Println(arr) //  &[7 2 3]
    }(&x)

    fmt.Println(x) //  [7 2 3]
}

: . , .

package main

import "fmt"

func main() {  
    x := []int{1,2,3}

    func(arr []int) {
        arr[0] = 7
        fmt.Println(arr) //  [7 2 3]
    }(x)

    fmt.Println(x) //  [7 2 3]
}

13. range


, for-in foreach . Go range , : — (item index), — (item data).

:

package main

import "fmt"

func main() {  
    x := []string{"a","b","c"}

    for v := range x {
        fmt.Println(v) //  0, 1, 2
    }
}

:

package main

import "fmt"

func main() {  
    x := []string{"a","b","c"}

    for _, v := range x {
        fmt.Println(v) //  a, b, c
    }
}

14.


, Go ? , . . — , .

, «» , « ».

, , .

«» . , . . , .

package main

func main() {  
    x := 2
    y := 4

    table := make([][]int,x)
    for i:= range table {
        table[i] = make([]int,y)
    }
}

« » . , «» , (raw data). — . , .

package main

import "fmt"

func main() {  
    h, w := 2, 4

    raw := make([]int,h*w)
    for i := range raw {
        raw[i] = i
    }
    fmt.Println(raw,&raw[4])
    // : [0 1 2 3 4 5 6 7] <ptr_addr_x>

    table := make([][]int,h)
    for i:= range table {
        table[i] = raw[i*w:i*w + w]
    }

    fmt.Println(table,&table[1][0])
    // : [[0 1 2 3] [4 5 6 7]] <ptr_addr_x>
}

, , , .

15. map


, nil- ( ). nil, « » — nil. . , - (map record), « ». (, , , « » — false). , , — , .

:

package main

import "fmt"

func main() {  
    x := map[string]string{"one":"a","two":"","three":"c"}

    if v := x["two"]; v == "" { // 
        fmt.Println("no entry")
    }
}

:

package main

import "fmt"

func main() {  
    x := map[string]string{"one":"a","two":"","three":"c"}

    if _,ok := x["two"]; !ok {
        fmt.Println("no entry")
    }
}

16.


, . — - (byte slices), . - , - .

:

package main

import "fmt"

func main() {  
    x := "text"
    x[0] = 'T'

    fmt.Println(x)
}

:

/tmp/sandbox305565531/main.go:7: cannot assign to x[0]

:

package main

import "fmt"

func main() {  
    x := "text"
    xbytes := []byte(x)
    xbytes[0] = 'T'

    fmt.Println(string(xbytes)) //  Text
}

, , . «» (rune). «» , (grave accent). «» , Go .

17. - (Byte Slices),


- ( ), . (cast operation), , (reslicing), , -.

Go []byte string string []byte, ( todo).

, []byte map[string]: m[string(key)].

for range, []byte: for i,v := range []byte(str) {...}.

18.


, , (byte value), ( ).

package main

import "fmt"

func main() {  
    x := "text"
    fmt.Println(x[0]) //  116
    fmt.Printf("%T",x[0]) //  uint8
}

«» ( / Unicode), for range. unicode/utf8 utf8string (golang.org/x/exp/utf8string). utf8string At(). (slice of runes).

19. — UTF-8


UTF-8. . , UTF-8, — . .

, ValidString() unicode/utf8.

package main

import (  
    "fmt"
    "unicode/utf8"
)

func main() {  
    data1 := "ABC"
    fmt.Println(utf8.ValidString(data1)) // : true

    data2 := "A\xfeC"
    fmt.Println(utf8.ValidString(data2)) // : false
}

20.


, Python :

data = u'♥'  
print(len(data)) # : 1  

Go, .

package main

import "fmt"

func main() {  
    data := "♥"
    fmt.Println(len(data)) // : 3
}

len() , , Unicode- Python.

Go, RuneCountInString() unicode/utf8.

package main

import (  
    "fmt"
    "unicode/utf8"
)

func main() {  
    data := "♥"
    fmt.Println(utf8.RuneCountInString(data)) // : 1

RuneCountInString() , .

package main

import (  
    "fmt"
    "unicode/utf8"
)

func main() {  
    data := "é"
    fmt.Println(len(data))                    // : 3
    fmt.Println(utf8.RuneCountInString(data)) // : 2
}

21. slice/array/map


:

package main

func main() {  
    x := []int{
    1,
    2 // error
    }
    _ = x
}

:

/tmp/sandbox367520156/main.go:6: syntax error: need trailing comma before newline in composite literal /tmp/sandbox367520156/main.go:8: non-declaration statement outside function body /tmp/sandbox367520156/main.go:9: syntax error: unexpected }

:

package main

func main() {  
    x := []int{
    1,
    2,
    }
    x = x

    y := []int{3,4,} //  
    y = y
}

, .

22. log.Fatal log.Panic


. , Go . Fatal*() Panic*(), .

package main

import "log"

func main() {  
    log.Fatalln("Fatal Level: log entry") //     
    log.Println("Normal Level: log entry")
}

23.


Go (concurrency), (concurrency safe). . , sync, .

24. range


( , range) — «» ( / Unicode), . «», . , . , norm (golang.org/x/text/unicode/norm).

for range UTF-8. - , 0xfffd ( Unicode), . ( UTF-8), -.

package main

import "fmt"

func main() {  
    data := "A\xfe\x02\xff\x04"
    for _,v := range data {
        fmt.Printf("%#x ",v)
    }
    // : 0x41 0xfffd 0x2 0xfffd 0x4 ()

    fmt.Println()
    for _,v := range []byte(data) {
        fmt.Printf("%#x ",v)
    }
    // : 0x41 0xfe 0x2 0xff 0x4 ()
}

25. - (map) for range


, , (, ). - . (runtime) Go , , , (, ) .

package main

import "fmt"

func main() {  
    m := map[string]int{"one":1,"two":2,"three":3,"four":4}
    for k,v := range m {
        fmt.Println(k,v)
    }
}

Go Playground (https://play.golang.org/), , , .

26. switch


case switch (break). : (fall through) case.

package main

import "fmt"

func main() {  
    isSpace := func(ch byte) bool {
        switch(ch) {
        case ' ': // 
        case '\t':
            return true
        }
        return false
    }

    fmt.Println(isSpace('\t')) //  true ()
    fmt.Println(isSpace(' '))  //  false ()
}

case fallthrough . switch, .

package main

import "fmt"

func main() {  
    isSpace := func(ch byte) bool {
        switch(ch) {
        case ' ', '\t':
            return true
        }
        return false
    }

    fmt.Println(isSpace('\t')) //  true ()
    fmt.Println(isSpace(' '))  //  true ()
}

27.


. Go . .

:

package main

import "fmt"

func main() {  
    data := []int{1,2,3}
    i := 0
    ++i // error
    fmt.Println(data[i++]) // 
}

:

/tmp/sandbox101231828/main.go:8: syntax error: unexpected ++ /tmp/sandbox101231828/main.go:9: syntax error: unexpected ++, expecting :

:

package main

import "fmt"

func main() {  
    data := []int{1,2,3}
    i := 0
    i++
    fmt.Println(data[i])
}

28. NOT-


~ NOT- (aka , bitwise complement), Go XOR- (^).

:

package main

import "fmt"

func main() {  
    fmt.Println(~2) // 
}

:

/tmp/sandbox965529189/main.go:6: the bitwise complement operator is ^

:

package main

import "fmt"

func main() {  
    var d uint8 = 2
    fmt.Printf("%08b\n",^d)
}

- , ^ Go — XOR-. , NOT- (, NOT 0x02) XOR- (, 0x02 XOR 0xff). , ^ NOT-.

Go AND NOT (&^), NOT. AND NOT / A AND (NOT B) .

package main

import "fmt"

func main() {  
    var a uint8 = 0x82
    var b uint8 = 0x02
    fmt.Printf("%08b [A]\n",a)
    fmt.Printf("%08b [B]\n",b)

    fmt.Printf("%08b (NOT B)\n",^b)
    fmt.Printf("%08b ^ %08b = %08b [B XOR 0xff]\n",b,0xff,b ^ 0xff)

    fmt.Printf("%08b ^ %08b = %08b [A XOR B]\n",a,b,a ^ b)
    fmt.Printf("%08b & %08b = %08b [A AND B]\n",a,b,a & b)
    fmt.Printf("%08b &^%08b = %08b [A 'AND NOT' B]\n",a,b,a &^ b)
    fmt.Printf("%08b&(^%08b)= %08b [A AND (NOT B)]\n",a,b,a & (^b))
}

29.


« » (bit clear) (&^), Go , . .

package main

import "fmt"

func main() {  
    fmt.Printf("0x2 & 0x2 + 0x4 -> %#x\n",0x2 & 0x2 + 0x4)
    //prints: 0x2 & 0x2 + 0x4 -> 0x6
    //Go:    (0x2 & 0x2) + 0x4
    //C++:    0x2 & (0x2 + 0x4) -> 0x2

    fmt.Printf("0x2 + 0x2 << 0x1 -> %#x\n",0x2 + 0x2 << 0x1)
    //prints: 0x2 + 0x2 << 0x1 -> 0x6
    //Go:     0x2 + (0x2 << 0x1)
    //C++:   (0x2 + 0x2) << 0x1 -> 0x8

    fmt.Printf("0xf | 0x2 ^ 0x2 -> %#x\n",0xf | 0x2 ^ 0x2)
    //prints: 0xf | 0x2 ^ 0x2 -> 0xd
    //Go:    (0xf | 0x2) ^ 0x2
    //C++:    0xf | (0x2 ^ 0x2) -> 0xf
}

30.


(struct fields), , (JSON, XML, GON . .), .

package main

import (  
    "fmt"
    "encoding/json"
)

type MyData struct {  
    One int
    two string
}

func main() {  
    in := MyData{1,"two"}
    fmt.Printf("%#v\n",in) //  main.MyData{One:1, two:"two"}

    encoded,_ := json.Marshal(in)
    fmt.Println(string(encoded)) //  {"One":1}

    var out MyData
    json.Unmarshal(encoded,&out)

    fmt.Printf("%#v\n",out) //  main.MyData{One:1, two:""}
}

31.


. . - — .

package main

import (  
    "fmt"
    "time"
)

func main() {  
    workerCount := 2

    for i := 0; i < workerCount; i++ {
        go doit(i)
    }
    time.Sleep(1 * time.Second)
    fmt.Println("all done!")
}

func doit(workerId int) {  
    fmt.Printf("[%v] is running\n",workerId)
    time.Sleep(3 * time.Second)
    fmt.Printf("[%v] is done\n",workerId)
}

:

[0] is running 
[1] is running 
all done!

WaitGroup. . , - , . kill. , : .

package main

import (  
    "fmt"
    "sync"
)

func main() {  
    var wg sync.WaitGroup
    done := make(chan struct{})
    workerCount := 2

    for i := 0; i < workerCount; i++ {
        wg.Add(1)
        go doit(i,done,wg)
    }

    close(done)
    wg.Wait()
    fmt.Println("all done!")
}

func doit(workerId int,done <-chan struct{},wg sync.WaitGroup) {  
    fmt.Printf("[%v] is running\n",workerId)
    defer wg.Done()
    <- done
    fmt.Printf("[%v] is done\n",workerId)
}

, :

[0] is running 
[0] is done 
[1] is running 
[1] is done

, . ! :

fatal error: all goroutines are asleep - deadlock!

! ? ? wg.Done(). .

, WaitGroup. wg.Done(), WaitGroup .

package main

import (  
    "fmt"
    "sync"
)

func main() {  
    var wg sync.WaitGroup
    done := make(chan struct{})
    wq := make(chan interface{})
    workerCount := 2

    for i := 0; i < workerCount; i++ {
        wg.Add(1)
        go doit(i,wq,done,&wg)
    }

    for i := 0; i < workerCount; i++ {
        wq <- i
    }

    close(done)
    wg.Wait()
    fmt.Println("all done!")
}

func doit(workerId int, wq <-chan interface{},done <-chan struct{},wg *sync.WaitGroup) {  
    fmt.Printf("[%v] is running\n",workerId)
    defer wg.Done()
    for {
        select {
        case m := <- wq:
            fmt.Printf("[%v] m => %v\n",workerId,m)
        case <- done:
            fmt.Printf("[%v] is done\n",workerId)
            return
        }
    }
}

.

32.


, . , , , .

package main

import "fmt"

func main() {  
    ch := make(chan string)

    go func() {
        for m := range ch {
            fmt.Println("processed:",m)
        }
    }()

    ch <- "cmd.1"
    ch <- "cmd.2" //   
}

33. panic


. ok (receive statement) false, , . , , , ok false.

panic. , , , .

package main

import (  
    "fmt"
    "time"
)

func main() {  
    ch := make(chan int)
    for i := 0; i < 3; i++ {
        go func(idx int) {
            ch <- (idx + 1) * 2
        }(i)
    }

    // get the first result
    fmt.Println(<-ch)
    close(ch) // (      )
    // do other work
    time.Sleep(2 * time.Second)
}

. — , . , .

, (special cancellation channel) , .

package main

import (  
    "fmt"
    "time"
)

func main() {  
    ch := make(chan int)
    done := make(chan struct{})
    for i := 0; i < 3; i++ {
        go func(idx int) {
            select {
            case ch <- (idx + 1) * 2: fmt.Println(idx,"sent result")
            case <- done: fmt.Println(idx,"exiting")
            }
        }(i)
    }

    // get first result
    fmt.Println("result:",<-ch)
    close(done)
    // do other work
    time.Sleep(3 * time.Second)
}

34. «nil»-


nil . , .

package main

import (  
    "fmt"
    "time"
)

func main() {  
    var ch chan int
    for i := 0; i < 3; i++ {
        go func(idx int) {
            ch <- (idx + 1) * 2
        }(i)
    }

    // get first result
    fmt.Println("result:",<-ch)
    // do other work
    time.Sleep(2 * time.Second)
}

runtime fatal error: all goroutines are asleep - deadlock!

case select.

package main

import "fmt"  
import "time"

func main() {  
    inch := make(chan int)
    outch := make(chan int)

    go func() {
        var in <- chan int = inch
        var out chan <- int
        var val int
        for {
            select {
            case out <- val:
                out = nil
                in = inch
            case val = <- in:
                out = outch
                in = nil
            }
        }
    }()

    go func() {
        for r := range outch {
            fmt.Println("result:",r)
        }
    }()

    time.Sleep(0)
    inch <- 1
    inch <- 2
    time.Sleep(3 * time.Second)
}

35. , ,


— . , / (receiver argument). , — - (map) — .

package main

import "fmt"

type data struct {  
    num int
    key *string
    items map[string]bool
}

func (this *data) pmethod() {  
    this.num = 7
}

func (this data) vmethod() {  
    this.num = 8
    *this.key = "v.key"
    this.items["vmethod"] = true
}

func main() {  
    key := "key.1"
    d := data{1,&key,make(map[string]bool)}

    fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
    // prints num=1 key=key.1 items=map[]

    d.pmethod()
    fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
    // prints num=7 key=key.1 items=map[]

    d.vmethod()
    fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
    // prints num=7 key=v.key items=map[vmethod:true]
}

36. HTTP-


HTTP-, HTTP-. , . : . , .

, .

package main

import (  
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {  
    resp, err := http.Get("https://api.ipify.org?format=json")
    defer resp.Body.Close()// 
    if err != nil {
        fmt.Println(err)
        return
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(body))
}

HTTP-, resp nil, runtime panic.

defer HTTP-.

package main

import (  
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {  
    resp, err := http.Get("https://api.ipify.org?format=json")
    if err != nil {
        fmt.Println(err)
        return
    }

    defer resp.Body.Close()// ,    :-)
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(body))
}

, HTTP-, resp nil, err — non-nil. non-nil. .

, non-nil HTTP-. : defer .

package main

import (  
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {  
    resp, err := http.Get("https://api.ipify.org?format=json")
    if resp != nil {
        defer resp.Body.Close()
    }

    if err != nil {
        fmt.Println(err)
        return
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(body))
}

resp.Body.Close() . HTTP- , keep alive. HTTP- . . , HTTP- . , Go 1.5.

HTTP-, - :

_, err = io.Copy(ioutil.Discard, resp.Body)

, , JSON API :

json.NewDecoder(resp.Body).Decode(&data)

37. HTTP-


HTTP- - ( HTTP 1.1 keep alive). HTTP- , HTTP-. / .

, true Close .

: Connection close. HTTP- Connection: close. , .

package main

import (  
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {  
    req, err := http.NewRequest("GET","http://golang.org",nil)
    if err != nil {
        fmt.Println(err)
        return
    }

    req.Close = true
    //or do this:
    //req.Header.Add("Connection", "close")

    resp, err := http.DefaultClient.Do(req)
    if resp != nil {
        defer resp.Body.Close()
    }

    if err != nil {
        fmt.Println(err)
        return
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(len(string(body)))
}

HTTP-. HTTP-.

package main

import (  
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {  
    tr := &http.Transport{DisableKeepAlives: true}
    client := &http.Client{Transport: tr}

    resp, err := client.Get("http://golang.org")
    if resp != nil {
        defer resp.Body.Close()
    }

    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(resp.StatusCode)

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(len(string(body)))
}

, . - , . . — .

38. (unmarshalling) JSON-


/ JSON- , Go JSON float64. , panic:

package main

import (  
  "encoding/json"
  "fmt"
)

func main() {  
  var data = []byte(`{"status": 200}`)

  var result map[string]interface{}
  if err := json.Unmarshal(data, &result); err != nil {
    fmt.Println("error:", err)
    return
  }

  var status = result["status"].(int) // 
  fmt.Println("status value:",status)
}

Runtime Panic:

panic: interface conversion: interface is float64, not int

JSON-, , , .


39. struct, array, slice map


== , .

package main

import "fmt"

type data struct {  
    num int
    fp float32
    complex complex64
    str string
    char rune
    yes bool
    events <-chan string
    handler interface{}
    ref *byte
    raw [10]byte
}

func main() {  
    v1 := data{}
    v2 := data{}
    fmt.Println("v1 == v2:",v1 == v2) // : v1 == v2: true
}

, . , , .

package main

import "fmt"

type data struct {  
    num int                // ok
    checks [10]func() bool // 
    doit func() bool       // 
    m map[string] string   // 
    bytes []byte           // 
}

func main() {  
    v1 := data{}
    v2 := data{}
    fmt.Println("v1 == v2:",v1 == v2)
}

Go , .

: DeepEqual() reflect.

package main

import (  
    "fmt"
    "reflect"
)

type data struct {  
    num int                // ok
    checks [10]func() bool // 
    doit func() bool       // 
    m map[string] string   // 
    bytes []byte           // 
}

func main() {  
    v1 := data{}
    v2 := data{}
    fmt.Println("v1 == v2:",reflect.DeepEqual(v1,v2)) // prints: v1 == v2: true

    m1 := map[string]string{"one": "a","two": "b"}
    m2 := map[string]string{"two": "b", "one": "a"}
    fmt.Println("m1 == m2:",reflect.DeepEqual(m1, m2)) // prints: m1 == m2: true

    s1 := []int{1, 2, 3}
    s2 := []int{1, 2, 3}
    fmt.Println("s1 == s2:",reflect.DeepEqual(s1, s2)) // prints: s1 == s2: true
}

( ), DeepEqual() .

package main

import (  
    "fmt"
    "reflect"
)

func main() {  
    var b1 []byte = nil
    b2 := []byte{}
    fmt.Println("b1 == b2:",reflect.DeepEqual(b1, b2)) // prints: b1 == b2: false
}

DeepEqual() nil-. , bytes.Equal(): nil .

package main

import (  
    "fmt"
    "bytes"
)

func main() {  
    var b1 []byte = nil
    b2 := []byte{}
    fmt.Println("b1 == b2:",bytes.Equal(b1, b2)) // prints: b1 == b2: true
}

DeepEqual() .

package main

import (  
    "fmt"
    "reflect"
    "encoding/json"
)

func main() {  
    var str string = "one"
    var in interface{} = "one"
    fmt.Println("str == in:",str == in,reflect.DeepEqual(str, in))
    //prints: str == in: true true

    v1 := []string{"one","two"}
    v2 := []interface{}{"one","two"}
    fmt.Println("v1 == v2:",reflect.DeepEqual(v1, v2))
    //prints: v1 == v2: false (not ok)

    data := map[string]interface{}{
        "code": 200,
        "value": []string{"one","two"},
    }
    encoded, _ := json.Marshal(data)
    var decoded map[string]interface{}
    json.Unmarshal(encoded, &decoded)
    fmt.Println("data == decoded:",reflect.DeepEqual(data, decoded))
    //prints: data == decoded: false (not ok)
}

- ( ) , , , ToUpper() ToLower() bytes strings ( ==, bytes.Equal() bytes.Compare()). , . strings.EqualFold() bytes.EqualFold().

- ( , . .), , reflect.DeepEqual(), bytes.Equal() bytes.Compare(). . , crypto/subtle (, subtle.ConstantTimeCompare()).

40. panic


recover() / panic. , defer.

:

package main

import "fmt"

func main() {  
    recover() //   
    panic("not good")
    recover() //    :)
    fmt.Println("ok")
}

:

package main

import "fmt"

func main() {  
    defer func() {
        fmt.Println("recovered:",recover())
    }()

    panic("not good")
}

recover() , defer.

:

package main

import "fmt"

func doRecover() {  
    fmt.Println("recovered =>",recover()) // prints: recovered => <nil>
}

func main() {  
    defer func() {
        doRecover() //  panic  
    }()

    panic("not good")
}

41. slice, array map for range


range — , . , . , , .

package main

import "fmt"

func main() {  
    data := []int{1,2,3}
    for _,v := range data {
        v *= 10 //   
    }

    fmt.Println("data:",data) // : [1 2 3]
}

, .

package main

import "fmt"

func main() {  
    data := []int{1,2,3}
    for i,_ := range data {
        data[i] *= 10
    }

    fmt.Println("data:",data) // : [10 20 30]
}

, . , . , , for range.

package main

import "fmt"

func main() {  
    data := []*struct{num int} {{1},{2},{3}}

    for _,v := range data {
        v.num *= 10
    }

    fmt.Println(data[0],data[1],data[2]) // prints &{10} &{20} &{30}
}

42. « »


. . , , .

package main

import "fmt"

func get() []byte {  
    raw := make([]byte,10000)
    fmt.Println(len(raw),cap(raw),&raw[0]) // : 10000 10000 <byte_addr_x>
    return raw[:3]
}

func main() {  
    data := get()
    fmt.Println(len(data),cap(data),&data[0]) // : 3 10000 <byte_addr_x>
}

, , ( ).

package main

import "fmt"

func get() []byte {  
    raw := make([]byte,10000)
    fmt.Println(len(raw),cap(raw),&raw[0]) // : 10000 10000 <byte_addr_x>
    res := make([]byte,3)
    copy(res,raw[:3])
    return res
}

func main() {  
    data := get()
    fmt.Println(len(data),cap(data),&data[0]) // : 3 3 <byte_addr_y>
}

43. «»


, ( ). , , , .

package main

import (  
    "fmt"
    "bytes"
)

func main() {  
    path := []byte("AAAA/BBBBBBBBB")
    sepIndex := bytes.IndexByte(path,'/')
    dir1 := path[:sepIndex]
    dir2 := path[sepIndex+1:]
    fmt.Println("dir1 =>",string(dir1)) // : dir1 => AAAA
    fmt.Println("dir2 =>",string(dir2)) // : dir2 => BBBBBBBBB

    dir1 = append(dir1,"suffix"...)
    path = bytes.Join([][]byte{dir1,dir2},[]byte{'/'})

    fmt.Println("dir1 =>",string(dir1)) // : dir1 => AAAAsuffix
    fmt.Println("dir2 =>",string(dir2)) // : dir2 => uffixBBBB (not ok)

    fmt.Println("new path =>",string(path))
}

. AAAAsuffix/BBBBBBBBB AAAAsuffix/uffixBBBB. , . . .

, . : (full slice expression).

package main

import (  
    "fmt"
    "bytes"
)

func main() {  
    path := []byte("AAAA/BBBBBBBBB")
    sepIndex := bytes.IndexByte(path,'/')
    dir1 := path[:sepIndex:sepIndex] //   
    dir2 := path[sepIndex+1:]
    fmt.Println("dir1 =>",string(dir1)) // : dir1 => AAAA
    fmt.Println("dir2 =>",string(dir2)) // : dir2 => BBBBBBBBB

    dir1 = append(dir1,"suffix"...)
    path = bytes.Join([][]byte{dir1,dir2},[]byte{'/'})

    fmt.Println("dir1 =>",string(dir1)) // : dir1 => AAAAsuffix
    fmt.Println("dir2 =>",string(dir2)) // : dir2 => BBBBBBBBB (ok now)

    fmt.Println("new path =>",string(path))
}

. .

44. «»


. , . , «» .

- , . ( ) .

import "fmt"

func main() {  
    s1 := []int{1,2,3}
    fmt.Println(len(s1),cap(s1),s1) //  3 3 [1 2 3]

    s2 := s1[1:]
    fmt.Println(len(s2),cap(s2),s2) //  2 2 [2 3]

    for i := range s2 { s2[i] += 20 }

    //       
    fmt.Println(s1) //  [1 22 23]
    fmt.Println(s2) //  [22 23]

    s2 = append(s2,4)

    for i := range s2 { s2[i] += 10 }

    //s1 is now "stale"
    fmt.Println(s1) //  [1 22 23]
    fmt.Println(s2) //  [32 33 14]
}

45.


( ), , .

:

package main

import "sync"

type myMutex sync.Mutex

func main() {  
    var mtx myMutex
    mtx.Lock() // 
    mtx.Unlock() // 
}

:

/tmp/sandbox106401185/main.go:9: mtx.Lock undefined (type myMutex has no field or method Lock) /tmp/sandbox106401185/main.go:10: mtx.Unlock undefined (type myMutex has no field or method Unlock)

, , .

:

package main

import "sync"

type myLocker struct {  
    sync.Mutex
}

func main() {  
    var lock myLocker
    lock.Lock() // ok
    lock.Unlock() // ok
}

.

:

package main

import "sync"

type myLocker sync.Locker

func main() {  
    var lock myLocker = new(sync.Mutex)
    lock.Lock() // ok
    lock.Unlock() // ok
}

46. for switch for select



break (label) switch/select. return — , — .

package main

import "fmt"

func main() {  
    loop:
        for {
            switch {
            case true:
                fmt.Println("breaking out...")
                break loop
            }
        }

    fmt.Println("out!")
}

goto

47. for


Go. for . , (aka ), for, ( , ).

:

package main

import (  
    "fmt"
    "time"
)

func main() {  
    data := []string{"one","two","three"}

    for _,v := range data {
        go func() {
            fmt.Println(v)
        }()
    }

    time.Sleep(3 * time.Second)
    //  : three, three, three
}

( ): for.

:

package main

import (  
    "fmt"
    "time"
)

func main() {  
    data := []string{"one","two","three"}

    for _,v := range data {
        vcopy := v //
        go func() {
            fmt.Println(vcopy)
        }()
    }

    time.Sleep(3 * time.Second)
    //  : one, two, three
}

: .

:

package main

import (  
    "fmt"
    "time"
)

func main() {  
    data := []string{"one","two","three"}

    for _,v := range data {
        go func(in string) {
            fmt.Println(in)
        }(v)
    }

    time.Sleep(3 * time.Second)
    //  : one, two, three
}

.

:

package main

import (  
    "fmt"
    "time"
)

type field struct {  
    name string
}

func (p *field) print() {  
    fmt.Println(p.name)
}

func main() {  
    data := []field{{"one"},{"two"},{"three"}}

    for _,v := range data {
        go v.print()
    }

    time.Sleep(3 * time.Second)
    //  : three, three, three
}

:

package main

import (  
    "fmt"
    "time"
)

type field struct {  
    name string
}

func (p *field) print() {  
    fmt.Println(p.name)
}

func main() {  
    data := []field{{"one"},{"two"},{"three"}}

    for _,v := range data {
        v := v
        go v.print()
    }

    time.Sleep(3 * time.Second)
    //  : one, two, three
}

, ( ), ?

package main

import (  
    "fmt"
    "time"
)

type field struct {  
    name string
}

func (p *field) print() {  
    fmt.Println(p.name)
}

func main() {  
    data := []*field{{"one"},{"two"},{"three"}}

    for _,v := range data {
        go v.print()
    }

    time.Sleep(3 * time.Second)
}

48. defer (Deferred Function Call Argument Evaluation)


, defer ( ).

package main

import "fmt"

func main() {  
    var i int = 1

    defer fmt.Println("result =>",func() int { return i * 2 }())
    i++
    //: result => 2 (not ok if you expected 4)
}

49. defer


, . , . , for, (defer) .

package main

import (  
    "fmt"
    "os"
    "path/filepath"
)

func main() {  
    if len(os.Args) != 2 {
        os.Exit(-1)
    }

    start, err := os.Stat(os.Args[1])
    if err != nil || !start.IsDir(){
        os.Exit(-1)
    }

    var targets []string
    filepath.Walk(os.Args[1], func(fpath string, fi os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        if !fi.Mode().IsRegular() {
            return nil
        }

        targets = append(targets,fpath)
        return nil
    })

    for _,target := range targets {
        f, err := os.Open(target)
        if err != nil {
            fmt.Println("bad target:",target,"error:",err) // : too many open files
            break
        }
        defer f.Close() //       
        //  -  ...
    }
}

— .

package main

import (  
    "fmt"
    "os"
    "path/filepath"
)

func main() {  
    if len(os.Args) != 2 {
        os.Exit(-1)
    }

    start, err := os.Stat(os.Args[1])
    if err != nil || !start.IsDir(){
        os.Exit(-1)
    }

    var targets []string
    filepath.Walk(os.Args[1], func(fpath string, fi os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        if !fi.Mode().IsRegular() {
            return nil
        }

        targets = append(targets,fpath)
        return nil
    })

    for _,target := range targets {
        func() {
            f, err := os.Open(target)
            if err != nil {
                fmt.Println("bad target:",target,"error:",err)
                return
            }
            defer f.Close() // ok
            //  -  ...
        }()
    }
}

: defer :-)

50.


« » , . , .

:

package main

import "fmt"

func main() {  
    var data interface{} = "great"

    if data, ok := data.(int); ok {
        fmt.Println("[is an int] value =>",data)
    } else {
        fmt.Println("[not an int] value =>",data)
        //: [not an int] value => 0 (not "great")
    }
}

:

package main

import "fmt"

func main() {  
    var data interface{} = "great"

    if res, ok := data.(int); ok {
        fmt.Println("[is an int] value =>",res)
    } else {
        fmt.Println("[not an int] value =>",data)
        // : [not an int] value => great (as expected)
    }
}

51.


«Go Concurrency Patterns» Google I/O 2012- concurrency-. — .

func First(query string, replicas ...Search) Result {  
    c := make(chan Result)
    searchReplica := func(i int) { c <- replicas[i](query) }
    for i := range replicas {
        go searchReplica(i)
    }
    return <-c
}

(replica) . . .

? ?

First() . , . . , (replica), .

, (exit). : , .

func First(query string, replicas ...Search) Result {  
    c := make(chan Result,len(replicas))
    searchReplica := func(i int) { c <- replicas[i](query) }
    for i := range replicas {
        go searchReplica(i)
    }
    return <-c
}

: select (case) default . default , , .

func First(query string, replicas ...Search) Result {  
    c := make(chan Result,1)
    searchReplica := func(i int) {
        select {
        case c <- replicas[i](query):
        default:
        }
    }
    for i := range replicas {
        go searchReplica(i)
    }
    return <-c
}

(special cancellation channel) .

func First(query string, replicas ...Search) Result {  
    c := make(chan Result)
    done := make(chan struct{})
    defer close(done)
    searchReplica := func(i int) {
        select {
        case c <- replicas[i](query):
        case <- done:
        }
    }
    for i := range replicas {
        go searchReplica(i)
    }

    return <-c
}

? (slides) . , , , .

52. , (pointer receiver),


(addressable), , . , , .

. - (map) . , , .

package main

import "fmt"

type data struct {  
    name string
}

func (p *data) print() {  
    fmt.Println("name:",p.name)
}

type printer interface {  
    print()
}

func main() {  
    d1 := data{"one"}
    d1.print() //ok

    var in printer = data{"two"} // 
    in.print()

    m := map[string]data {"x":data{"three"}}
    m["x"].print() //
}

:

/tmp/sandbox017696142/main.go:21: cannot use data literal (type data) as type printer in assignment: data does not implement printer (print method has pointer receiver)
/tmp/sandbox017696142/main.go:25: cannot call pointer method on m["x"] /tmp/sandbox017696142/main.go:25: cannot take the address of m["x"]

53. -


, , .

:

package main

type data struct {  
    name string
}

func main() {  
    m := map[string]data {"x":{"one"}}
    m["x"].name = "two" // error
}

:

/tmp/sandbox380452744/main.go:9: cannot assign to m["x"].name

, .

, — .

package main

import "fmt"

type data struct {  
    name string
}

func main() {  
    s := []data {{"one"}}
    s[0].name = "two" // ok
    fmt.Println(s)    // prints: [{two}]
}

, - (gccgo) . :-) , Go 1.3. , todo.

: .

package main
import "fmt"

type data struct {  
    name string
}

func main() {  
    m := map[string]data {"x":{"one"}}
    r := m["x"]
    r.name = "two"
    m["x"] = r
    fmt.Printf("%v",m) //: map[x:{two}]
}

: - .

package main

import "fmt"

type data struct {  
    name string
}

func main() {  
    m := map[string]*data {"x":{"one"}}
    m["x"].name = "two" //ok
    fmt.Println(m["x"]) //: &{two}
}

, , ?

package main

type data struct {  
    name string
}

func main() {  
    m := map[string]*data {"x":{"one"}}
    m["z"].name = "what?" //???
}

54. nil- nil-


Go. — , . nil , nil.

, . , nil, .

package main

import "fmt"

func main() {  
    var data *byte
    var in interface{}

    fmt.Println(data,data == nil) // : <nil> true
    fmt.Println(in,in == nil)     // : <nil> true

    in = data
    fmt.Println(in,in == nil)     // : <nil> false
    //'data'  'nil',  'in' —  'nil'
}

, , .

:

package main

import "fmt"

func main() {  
    doit := func(arg int) interface{} {
        var result *struct{} = nil

        if(arg > 0) {
            result = &struct{}{}
        }

        return result
    }

    if res := doit(-1); res != nil {
        fmt.Println("good result:",res) // : good result: <nil>
        // 'res'   'nil',    — 'nil'
    }
}

:

package main

import "fmt"

func main() {  
    doit := func(arg int) interface{} {
        var result *struct{} = nil

        if(arg > 0) {
            result = &struct{}{}
        } else {
            return nil //   'nil'
        }

        return result
    }

    if res := doit(-1); res != nil {
        fmt.Println("good result:",res)
    } else {
        fmt.Println("bad result (res is nil)") //  —   
    }
}

55.


, . ++ new, . Go , new() make(). « » (escape analysis). , , , ++.

, , -gcflags -m go build go run (, go run -gcflags -m app.go).

56. GOMAXPROCS, (concurrency)


Go 1.4 / . , . Go 1.5 , runtime.NumCPU(). , CPU . , GOMAXPROCS runtime.GOMAXPROCS().

, GOMAXPROCS , Go . runtime.GOMAXPROCS() . GOMAXPROCS (https://golang.org/pkg/runtime/) .

GOMAXPROCS , — 256.

package main

import (  
    "fmt"
    "runtime"
)

func main() {  
    fmt.Println(runtime.GOMAXPROCS(-1)) // : X (1 on play.golang.org)
    fmt.Println(runtime.NumCPU())       // : X (1 on play.golang.org)
    runtime.GOMAXPROCS(20)
    fmt.Println(runtime.GOMAXPROCS(-1)) // : 20
    runtime.GOMAXPROCS(300)
    fmt.Println(runtime.GOMAXPROCS(-1)) // : 256
}

57.


Go , , , . .

package main

import (  
    "runtime"
    "time"
)

var _ = runtime.GOMAXPROCS(3)

var a, b int

func u1() {  
    a = 1
    b = 2
}

func u2() {  
    a = 3
    b = 4
}

func p() {  
    println(a)
    println(b)
}

func main() {  
    go u1()
    go u2()
    go p()
    time.Sleep(1 * time.Second)
}

, a b:

1 
2

3 
4

0 
2

0 
0

1 
4

— 02 — , b a.

, sync.

58. (Preemptive Scheduling)


(rogue) , . , for, .

package main

import "fmt"

func main() {  
    done := false

    go func(){
        done = true
    }()

    for !done {
    }
    fmt.Println("done!")
}

for . , , .

, go, , . , (non-inlined) .

package main

import "fmt"

func main() {  
    done := false

    go func(){
        done = true
    }()

    for !done {
        fmt.Println("not done!") //  
    }
    fmt.Println("done!")
}

, , -gcflags –m go build go run (, go build -gcflags -m).

: . Gosched() runtime.

package main

import (  
    "fmt"
    "runtime"
)

func main() {  
    done := false

    go func(){
        done = true
    }()

    for !done {
        runtime.Gosched()
    }
    fmt.Println("done!")
}

, Reddit ( , . — . .).

Source: https://habr.com/ru/post/314804/


All Articles