8

[TOC]

31 pits of go language

The resources come from the following links:

http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#opening_braces

He looks like this after opening:

After understanding and operating them one by one, the following 31 22 GOLANG pits were selected and shared with you

1. The opening brace cannot be placed on a line alone {

In most other languages, { is up to you. Go is more special. obeys the semicolon injection rules (automatic semicolon injection) : The compiler will add after a specific separator at the end of each line of code; to separate multiple statements, for example, it will add a semicolon after )

// 错误示例
func main()                    
{
    println("www.topgoer.com是个不错的go语言中文文档")
}

// 等效于
func main();    // 无函数体                    
{
    println("hello world")
}

// 正确示例
func main() {
    println("Golang新手可能会踩的50个坑")
}

The compilation error of the above error example is as follows:

2. You cannot use short declarations to set the value of a field

The variable field of struct := to use predefined variables to avoid solving:

// 错误示例
package main

import "fmt"

type info struct {
    result int
}

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

func main() {
    var data info
    data.result, err := work() // error: non-name data.result on left side of :=
    if err != nil{
        fmt.Println(err)
        return
    }
    fmt.Printf("info: %+v\n", data)
}



// 正确示例
func work() (int, error) {
    return 3, nil
}

func main() {

    tmp, err := work() // error: non-name data.result on left side of :=
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("info: %+v\n", tmp)
}

The above error examples are as follows:

3. Accidentally overwrite variables

For developers who switch from a dynamic language, the short statement is very useful, which may lead people to misunderstand that := is an assignment operator. := in a new code block like the following, the compilation will not report an error, but the variables will not work as you expect:

func main() {
    x := 1
    println(x)        // 1
    {
        println(x)    // 1
        x := 2
        println(x)    // 2    // 新的 x 变量的作用域只在代码块内部
    }
    println(x)        // 1
}

This is a common mistake made by Go developers, and it is not easy to find. You can use the vet tool to diagnose this type of variable coverage. Go does not do coverage checking by default. Add the -shadow option to enable it:

    > go tool vet -shadow main.go
    main.go:9: declaration of "x" shadows declaration at main.go:5

Note that vet will not report all covered variables, you can use go-nyet to do further testing:

    > $GOPATH/bin/go-nyet main.go
    main.go:10:3:Shadowing variable `x`

4. Explicitly typed variables cannot be initialized with nil

nil is the default initial value of 6 kinds of However, the type is not specified in the declaration, and the compiler cannot infer the specific type of the variable.

  • interface
  • function
  • pointer
  • map
  • slice
  • channel
// 错误示例
func main() {
    var x = nil    // error: use of untyped nil
    _ = x
}


// 正确示例
func main() {
    var x interface{} = nil
    _ = x
}

5. Directly use slice and map whose value is nil

  • Allow adding elements to slices whose value is nil

    Because slices are implemented in a way similar to c++ vectors, dynamically expanding memory

  • Adding elements to a map with a value of nil will cause panic at runtime

    The initialization of the map must allocate memory, otherwise an error will be reported directly

// map 错误示例
func main() {
    var m map[string]int
    m["one"] = 1        // error: panic: assignment to entry in nil map
    // m := make(map[string]int)// map 的正确声明,分配了实际的内存
}    


// slice 正确示例
func main() {
    var s []int
    s = append(s, 1)
}

func main() {
    //m := map[string]int{}
    m := make(map[string]int, 1)
    m["one"] = 1
}

6.map capacity

You can specify the capacity when creating a variable of the map type, but you cannot use cap() to detect the size of the allocated space like slices:

// 错误示例
func main() {
    m := make(map[string]int, 99)
    println(cap(m))     // error: invalid argument m1 (type map[string]int) for cap  
}

According to the official document, the following types can be put in the cap function parameter:

  • array
  • pointer
  • sliice
  • channel

7. The variable value of string type cannot be nil

For those who like to initialize strings with nil, this is the pit:

The initialization string is empty, you can use var to define directly, the default is empty ""

// 错误示例
func main() {
    var s string = nil    // cannot use nil as type string in assignment
    if s == nil {    // invalid operation: s == nil (mismatched types string and nil)
        s = "default"
    }
}


// 正确示例
func main() {
    var s string    // 字符串类型的零值是空串 ""
    if s == "" {
        s = "default"
    }
}

There are 6 types that can be initialized to nil, which are also mentioned above

  • pointer
  • aisle
  • function
  • interface
  • map
  • slice

8. Array type value as function parameter

In C/C++, an array (name) is a pointer. When passing an array as a parameter into a function, it is equivalent to passing a reference to the memory address of the array, and the value of the array will be changed inside the function.

In Go , the array is the value. When passed into the function as a parameter, it is a copy of the original value of the array. At this time, the array cannot be updated inside the function:

// 数组使用值拷贝传参
func main() {
    x := [3]int{3,4,5}

    func(arr [3]int) {
        arr[0] = 8
        fmt.Println(arr)    // [8 4 5]
    }(x)
    fmt.Println(x)            // [3 4 5]    // 并不是你以为的 [8 4 5]
}

If you want to modify the value of the original array in the parameter, there are two ways:

  • Pass the pointer type directly to this array
// 传址会修改原数据
func main() {
    x := [3]int{3,4,5}

    func(arr *[3]int) {
        (*arr)[0] = 8    
        fmt.Println(arr)    // &[8 4 5]
    }(&x)
    fmt.Println(x)    // [8 4 5]
}
  • Use slice directly: Even if the value of the slice is copied inside the function, the original data of the slice (the underlying array) will still be updated

    Because slice is passed by reference

// 会修改 slice 的底层 array,从而修改 slice
func main() {
    x := []int{1, 2, 3}
    func(arr []int) {
        arr[0] = 7
        fmt.Println(x)    // [8 4 5]
    }(x)
    fmt.Println(x)    // [8 4 5]
}

Golang is divided into value types and reference types

  • The value types are

    int series, float series, bool, string, array and structure

  • The reference types are:

    pointer, slice slice, pipe channel, interface interface, map, function, etc.

  • The characteristics of value types are

    Variables store values directly, and memory is usually allocated on the stack

  • The characteristics of reference types are

    The variable stores an address, and the space corresponding to this address is the real value stored. The memory is usually allocated in the heap

9. Access keys that do not exist in the map

Similar to other programming languages, if you access a key that does not exist in the map, you hope to return nil,

Go will return the zero value of the corresponding data type of the element, such as nil,'', false and 0 . Value operation always returns a value, so you cannot judge whether the key is in the map by the value taken out.

  • For value type : Boolean type is false , numeric type is 0 , string is ""

    • Arrays and structures will recursively initialize their elements or fields
    • Its initial value depends on the element type or field
  • For reference type : are all nil , including pointer, function, interface, slice, pipe channel, mapping map.

To check if the key exists, you can use map to directly access it, just check the returned second parameter:

// 错误的 key 检测方式
func main() {
    x := map[string]string{"one": "2", "two": "", "three": "3"}
    if v := x["two"]; v == "" {
        fmt.Println("key two is no entry")    // 键 two 存不存在都会返回的空字符串
    }
}

// 正确示例
func main() {
    x := map[string]string{"one": "2", "two": "", "three": "3"}
    if _, ok := x["two"]; !ok {
        fmt.Println("key two is no entry")
    }
}

10. The value of string type is constant and cannot be changed. You can use rune to convert

Trying to use the index to traverse the string to update individual characters in the string is not allowed, because the value of the string type is constant

The solution is divided into two types: English string and Chinese string

  • English string

    The value of the string type is a read-only binary byte slice. After the string is changed to []byte, it can be converted to string.

// 修改字符串的错误示例
func main() {
    x := "text"
    x[0] = "T"        // error: cannot assign to x[0]
    fmt.Println(x)
}


// 修改示例
func main() {
    x := "text"
    xBytes := []byte(x)
    xBytes[0] = 'T'    // 注意此时的 T 是 rune 类型
    x = string(xBytes)
    fmt.Println(x)    // Text
}
  • Chinese string

    A UTF8-encoded character may occupy multiple bytes. For example, Chinese characters need 3~4 bytes to store . At this time, you need to use the following approach, using rune slice

    Convert string to rune slice (in this case, one rune may occupy multiple bytes), and directly update the characters in rune

func main() {
    x := "text"
    xRunes := []rune(x)
    xRunes[0] = '你'
    x = string(xRunes)
    fmt.Println(x)    // 你ext
}

11.string and index operators

Index access to a string returns not a character, but a byte value.

If you need to use for range iteratively access the characters in the string ( unicode code point / rune ), there is a "unicode/utf8" package in the standard library to do the relevant decoding and encoding of UTF8 In addition, utf8string also has convenient library functions func (s *String) At(i int) rune

12. Strings are not all UTF8 text

The value of string does not have to be UTF8 text, it can contain any value. It is UTF8 text only when the string is a literal value, and the string can contain other data by escaping.

To determine whether the string is UTF8 text, you can use the ValidString() function in the "unicode/utf8" package:

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

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

    str3 := "A\\xfeC"
    fmt.Println(utf8.ValidString(str3))    // true    // 把转义字符转义成字面值
}

13. The length of the string

In Go:

Use len function calculate the length of the string, which is actually to calculate the number of bytes

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

If you want to get the number of characters in the string, you can use RuneCountInString (str string) (n int) in the "unicode/utf8" package

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

Note: RuneCountInString does not always return the number of characters we see, because some characters will take up 2 runes:

func main() {
    char := "é"
    fmt.Println(len(char))    // 3
    fmt.Println(utf8.RuneCountInString(char))    // 2
    fmt.Println("cafe\u0301")    // café    // 法文的 cafe,实际上是两个 rune 的组合
}

14.range the value obtained by iterating the string

The index obtained by range is the position of the first byte of the character value (Unicode point / rune). Unlike other programming languages, this index is not directly the position of the character in the string.

Note that one character may occupy multiple café , such as 06082f611a7fd8 in the é . Operation special characters can use the norm package.

The for range iteration will try to translate string into UTF8 text, and directly use 0XFFFD rune(�) UNicode substitute characters for any invalid code points. If there is any non-UTF8 data in the string, you should save the string as a byte slice before performing the operation.

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

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

15. The fallthrough statement in switch

The case code block in the switch statement default, but you can use fallthrough to force the execution of the next case code block.

func main() {
    isSpace := func(char byte) bool {
        switch char {
        case ' ':    // 空格符会直接 break,返回 false // 和其他语言不一样
        // fallthrough    // 返回 true
        case '\t':
            return true
        }
        return false
    }
    fmt.Println(isSpace('\t'))    // true
    fmt.Println(isSpace(' '))    // false
}

fallthrough at the end of the case code block to force the execution of the next case code block.

16.Bitwise negation

Go reuses ^ XOR operator for bitwise inversion:

// 错误的取反操作
func main() {
    fmt.Println(~2)        // bitwise complement operator is ^
}


// 正确示例
func main() {
    var d uint8 = 2
    fmt.Printf("%08b\n", d)        // 00000010
    fmt.Printf("%08b\n", ^d)    // 11111101
}

At the same time, ^ is also a bitwise exclusive OR (XOR) operator.

An operator can be reused twice because the unary NOT operation NOT 0x02 is consistent with the binary XOR operation 0x22 XOR 0xff.

Go also has a special operator AND NOT, &^ operator, which takes 1 for different bits.

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))
}
    10000010 [A]
    00000010 [B]
    11111101 (NOT B)
    00000010 ^ 11111111 = 11111101 [B XOR 0xff]
    10000010 ^ 00000010 = 10000000 [A XOR B]
    10000010 & 00000010 = 00000010 [A AND B]
    10000010 &^00000010 = 10000000 [A 'AND NOT' B]
    10000010&(^00000010)= 10000000 [A AND (NOT B)]

17. Operator precedence

In addition to the bit clear operator, Go also has many bit operators that are the same as other languages, but there are some differences in precedence.

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
}

Priority list:

    Precedence    Operator
        5             *  /  %  <<  >>  &  &^
        4             +  -  |  ^
        3             ==  !=  <  <=  >  >=
        2             &&
        1             ||

18. Struct fields that are not exported cannot be encoded

In GOLANG

  • Field members starting with lowercase letters cannot be directly accessed by the outside
  • Field members starting with capital letters can be directly accessed from the outside

So when struct is performing json、xml , if it needs to be used normally, the initial letter of the member must be capitalized, otherwise these private fields will be ignored and a zero value will be obtained when exporting

package main

import (
    "encoding/json"
    "fmt"
)

type MyInfo struct {
    Name string
    age int
}

func main() {
    in := MyInfo{"小魔童", 18}
    fmt.Printf("%#v\n", in) // main.MyData{Name:"小魔童", age:18}

    encoded, _ := json.Marshal(in)
    fmt.Println(string(encoded)) // {Name:"小魔童"}    // 私有字段 age 被忽略了

    var out MyInfo
    json.Unmarshal(encoded, &out)
    fmt.Printf("%#v\n", out) // main.MyData{Name:"小魔童", age:0}
}

19. Sending data to a closed channel will cause panic

  • It is safe to receive data from the closed channel . When the receiving status value ok is false, it means that there is no data in the channel to receive
  • Receive data from the buffered channel , when the buffered data is acquired and there is no more data to fetch, the status value is also false
  • Sending data to the closed channel will cause panic
func main() {
   ch := make(chan int)

   for i := 0; i < 3; i++ {
      go func(idx int) {
         fmt.Println("i == ", idx)
         select {
         case ch <- (idx + 1) * 2:
            fmt.Println(idx, "Send result")
         }
      }(i)
   }

   fmt.Println("Result: ", <-ch)
   close(ch)
   fmt.Println("-----close ch----")
   time.Sleep(3 * time.Second)
}

There are also solutions to the above problems

can use a discarded channel done to tell the remaining goroutines that there is no need to send data to ch . At this time, the result of <- done is {}:

func main() {
   ch := make(chan int)
   done := make(chan struct{})

   for i := 0; i < 3; i++ {
      go func(idx int) {
         fmt.Println("i == ", idx)
         select {
         case ch <- (idx + 1) * 2:
            fmt.Println(idx, "Send result")
         case <-done:
            fmt.Println(idx, "Exiting")
         }
      }(i)
   }

   fmt.Println("Result: ", <-ch)
   close(done)
   fmt.Println("-----close done----")
   time.Sleep(3 * time.Second)
}

20. If the function receiver parameter is passed by value, the original value of the parameter cannot be modified

The parameters of method receiver are similar to those of ordinary functions: if declared as a value, the method body will get a copy of the value of the parameter, and any modification of the parameter will not affect the original value.

Unless the receiver parameter is a variable of map or slice type, and the fields in the map and the elements in the slice are updated in a pointer mode, the original value will be updated:

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

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

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

func main() {
    key := "key1"

    d := data{1, &key, make(map[string]bool)}
    fmt.Printf("num=%v  key=%v  items=%v\n", d.num, *d.key, d.items)

    d.pointerFunc()    // 修改 num 的值为 7
    fmt.Printf("num=%v  key=%v  items=%v\n", d.num, *d.key, d.items)

    d.valueFunc()    // 修改 key 和 items 的值
    fmt.Printf("num=%v  key=%v  items=%v\n", d.num, *d.key, d.items)
}
  • valueFunc function, the data passed is the value, which is a copy of , and

    • num is a copy, so the original value has not been changed
    • key is the passed pointer, so the original value will be changed
    • items is a map, which is passed by reference, so it will also be changed
  • In the pointerFunc function, data is the transfer address, so the original value of num can be changed

21. Turn off the HTTP response body

When using the HTTP standard library to initiate a request and get a response, even if you do not read any data from the response or the response is empty, you need to manually close the response body. There are the following pits about the http request and response

  • Request http response, close the response body position is wrong

    The following code can initiate the request correctly, but once the request fails, the value of the variable resp is nil, causing panic

    Because resp is nil, resp.Body.Close() will go to the body from nil and then close, and cannot read a section of memory from an empty address, so it will panic

// 请求失败造成 panic
func main() {
    resp, err := http.Get("https://api.ipify.org?format=json")
    defer resp.Body.Close()    // resp 可能为 nil,不能读取 Body
    if err != nil {
        fmt.Println(err)
        return
    }

    body, err := ioutil.ReadAll(resp.Body)
    checkError(err)

    fmt.Println(string(body))
}

func checkError(err error) {
    if err != nil{
        log.Fatalln(err)
    }
}
  • correct approach is

    First check that the HTTP response error is nil, and then call resp.Body.Close() to close the response body:

// 大多数情况正确的示例
func main() {
    resp, err := http.Get("https://api.ipify.org?format=json")
    checkError(err)

    defer resp.Body.Close()    // 绝大多数情况下的正确关闭方式
    body, err := ioutil.ReadAll(resp.Body)
    checkError(err)

    fmt.Println(string(body))
}
  • There will also be a redirection error. The resp and err returned by the http request are not empty. So how to deal with it, there are two ways

1. You can directly close the non-nil response body in the code block that handles the HTTP response error.

2. Manually call defer to close the response body:

// 正确示例
func main() {
    resp, err := http.Get("http://www.baidu.com")

    // 关闭 resp.Body 的正确姿势
    if resp != nil {
        defer resp.Body.Close()
    }

    checkError(err)
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    checkError(err)

    fmt.Println(string(body))
}

resp.Body.Close() The earlier version of 16082f611ac8a3 is to read the response body data and discard it, which ensures that the keep-alive HTTP connection can be reused to process more than one request.

But the latest version of Go gives the user the task of reading and discarding data. If you don't handle it, the HTTP connection may be closed directly instead of being reused. Please refer to the Go 1.5 version of the document.

If the program reuses a lot of HTTP persistent connections, you may want to add in the logic code for processing the response:

    _, err = io.Copy(ioutil.Discard, resp.Body) // 手动丢弃读取完毕的数据

If you need to read the response completely, the code above needs to be written. For example, when decoding the JSON response data of the API:

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

22. Close the HTTP connection

that support 16082f611ac920 HTTP1.1 or HTTP1.0 configured with the connection: keep-alive option will maintain a long connection for a period of time. However, "net/http" is only disconnected when the server actively asks to be closed by default, so your program may run out of socket descriptors. solution has two , after the request is over:

  • Directly set the Close field value of the request variable to true, and the connection will be actively closed after each request.
  • Set the Header request header option Connection: close , and then the response header returned by the server will also have this option. At this time, the HTTP standard library will actively disconnect.
// 主动关闭连接
func main() {
    req, err := http.NewRequest("GET", "http://golang.org", nil)
    checkError(err)

    req.Close = true
    //req.Header.Add("Connection", "close")    // 等效的关闭方式

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

    body, err := ioutil.ReadAll(resp.Body)
    checkError(err)

    fmt.Println(string(body))
}

23. Value comparison of struct, array, slice and map

You can use the equality operator == to compare structure variables, provided that the members of the two structures are of comparable types:

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)    // true
}

If any member of the two structures is not comparable, it will cause a compilation error. Note that array members can only be compared when the array elements are comparable.

type data struct {
    num    int
    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)
}
invalid operation: v1 == v2 (struct containing [10]func() bool cannot be compared)

Go provides a number of library functions that can not be used to compare == variable comparisons, such as using "reflect" package DeepEqual() :

// 比较相等运算符无法比较的元素
func main() {
    v1 := data{}
    v2 := data{}
    fmt.Println("v1 == v2: ", reflect.DeepEqual(v1, v2))    // true

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

    s1 := []int{1, 2, 3}
    s2 := []int{1, 2, 3}
       // 注意两个 slice 相等,值和顺序必须一致
    fmt.Println("v1 == v2: ", reflect.DeepEqual(s1, s2))    // true
}

This comparison method may be slower, and use it according to your program requirements. DeepEqual() has other usages:

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

note:

  • DeepEqual() is not always suitable for comparison slice
func main() {
    var str = "one"
    var in interface{} = "one"
    fmt.Println("str == in: ", reflect.DeepEqual(str, in))    // true

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

    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))    // false
}

If you want to compare the English text in byte or string with case insensitivity, you can use the ToUpper() and ToLower() functions of the "bytes" or "strings" package. To compare byte or string in other languages, you should use bytes.EqualFold() and strings.EqualFold()

If the byte slice contains data to verify the user's identity (ciphertext hash, token, etc.), reflect.DeepEqual(), bytes.Equal(), bytes.Compare() should not be used. These three functions are easy to cause timing attacks to the program. In this case, the subtle.ConstantTimeCompare() functions in the "crypto/subtle" package should be used

  • reflect.DeepEqual() considers that empty slice and nil slice are not equal, but note that byte.Equal() will consider the two to be equal:
func main() {
    var b1 []byte = nil
    b2 := []byte{}

    // b1 与 b2 长度相等、有相同的字节序
    // nil 与 slice 在字节上是相同的
    fmt.Println("b1 == b2: ", bytes.Equal(b1, b2))    // true
}

24. Recover from panic

In a the defer calling function delays execution Recover () , it will be able to Capture / interrupt panic

// 错误的 recover 调用示例
func main() {
    recover()    // 什么都不会捕捉
    panic("not good")    // 发生 panic,主程序退出
    recover()    // 不会被执行
    println("ok")
}

// 正确的 recover 调用示例
func main() {
    defer func() {
        fmt.Println("recovered: ", recover())
    }()
    panic("not good")
}

As can be seen from the above, recover() will only take effect when called in a function executed by defer.

25. Update the elements by updating the reference when the range iterates over slice, array, and map

, the value obtained is actually a copy of the value of the element. Updating the copy will not change the original element, that is, the address of the copy is not the address of the original element:

func main() {
    data := []int{1, 2, 3}
    for _, v := range data {
        v *= 10        // data 中原有元素是不会被修改的
    }
    fmt.Println("data: ", data)    // data:  [1 2 3]
}

If you want to modify the value of the original element, you should use the index to directly access:

func main() {
    data := []int{1, 2, 3}
    for i, v := range data {
        data[i] = v * 10    
    }
    fmt.Println("data: ", data)    // data:  [10 20 30]
}

If your collection holds a pointer to a value, you need to modify it slightly. You still need to use the index to access the element, but you can use the element from the range to directly update the original value:

func main() {
    data := []*struct{ num int }{{1}, {2}, {3},}
    for _, v := range data {
        v.num *= 10    // 直接使用指针更新
    }
    fmt.Println(data[0], data[1], data[2])    // &{10} &{20} &{30}
}

26. Old slice

When you create a new slice from an existing slice , the two data points to the same underlying array. If your program uses this feature, you need to pay attention to the "stale" slice problem.

some cases, when an element is added to a slice and the capacity of the underlying array it points to is insufficient

will reallocate a new array to store data . The other slices also point to the original old underlying array.

// 超过容量将重新分配数组来拷贝值、重新存储
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
    }
    // 此时的 s1 与 s2 是指向同一个底层数组的
    fmt.Println(s1)        // [1 22 23]
    fmt.Println(s2)        // [22 23]

    s2 = append(s2, 4)    // 向容量为 2 的 s2 中再追加元素,此时将分配新数组来存

    for i := range s2 {
        s2[i] += 10
    }
    fmt.Println(s1)        // [1 22 23]    // 此时的 s1 不再更新,为旧数据
    fmt.Println(s2)        // [32 33 14]
}

27. Jump out of the for-switch and for-select code blocks

break without a specified label will only jump out of the switch/select statement. If the return statement jumps out, you can break out of the code block specified by the label

// break 配合 label 跳出指定代码块
func main() {
loop:
    for {
        switch {
        case true:
            fmt.Println("breaking out...")
            //break    // 死循环,一直打印 breaking out...
            break loop
        }
    }
    fmt.Println("out...")
}

goto can also jump to the specified position, but it will still enter the for-switch again, in an endless loop.

28. Parameter value of defer function

For a function whose execution is delayed by defer, its parameters will be evaluated at the time of declaration, rather than evaluated at the time of execution:

// 在 defer 函数中参数会提前求值
func main() {
    var i = 1
    defer fmt.Println("result: ", func() int { return i * 2 }())
    i++
}

29. Execution timing of defer function

For defer delayed execution function, will be executed at the end of the function calling it, instead of executing at the end of the statement block that called it, pay attention to the distinction.

For example, in a long-running function, defer is used in the internal for loop to clean up the resource calls generated by each iteration. You need to put defer in an anonymous function so that there is no problem.

// 目录遍历正常
func main() {
    // ...

    for _, target := range targets {
        func() {
            f, err := os.Open(target)
            if err != nil {
                fmt.Println("bad target:", target, "error:", err)
                return    // 在匿名函数内使用 return 代替 break 即可
            }
            defer f.Close()    // 匿名函数执行结束,调用关闭文件资源

            // 使用 f 资源
        }()
    }
}

30. Update the value of the map field

  • The elements in the map are not addressable

    If map is of struct type, you cannot directly update a single field of the struct

// 无法直接更新 struct 的字段值
type data struct {
    name string
}

func main() {
    m := map[string]data{
        "x": {"Tom"},
    }
    m["x"].name = "Jerry"
}
cannot assign to struct field m["x"].name in map
  • The elements of slice are addressable:
type data struct {
    name string
}

func main() {
    s := []data{{"Tom"}}
    s[0].name = "Jerry"
    fmt.Println(s)    // [{Jerry}]
}

Of course, there are still methods to update the field value of the struct element in the map, as follows:

  • Use local variables

    The most value is directly processed by assignment

// 提取整个 struct 到局部变量中,修改字段值后再整个赋值
type data struct {
    name string
}

func main() {
    m := map[string]data{
        "x": {"Tom"},
    }
    r := m["x"]
    r.name = "Jerry"
    m["x"] = r
    fmt.Println(m)    // map[x:{Jerry}]
}
  • Use map pointer to element

    The pointer is used directly, no addressing is required

func main() {
    m := map[string]*data{
        "x": {"Tom"},
    }

    m["x"].name = "Jerry"    // 直接修改 m["x"] 中的字段
    fmt.Println(m["x"])    // &{Jerry}
}

But pay attention to the following misuse:

The following problem occurs because the internal m["z"] did not open the memory of the response data structure, so there will be a memory leak problem

func main() {
    m := map[string]*data{
        "x": {"Tom"},
    }
    m["z"].name = "what???"     
    fmt.Println(m["x"])
}
panic: runtime error: invalid memory address or nil pointer dereference

31.nil interface and nil interface value

Although interface looks like a pointer type, it is not. interface nil when the type and value are both nil

If the value of your interface variable changes with other variables, be careful when comparing equal to nil:

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
}

If the return value type of your function is interface , be careful of this pit:

// 错误示例
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>
        fmt.Printf("%T\n", res)            // *struct {}    // res 不是 nil,它的值为 nil
        fmt.Printf("%v\n", res)            // <nil>
    }
}


// 正确示例
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)    // Bad result:  <nil>
    }
}

The above is the entire contents of the current period, in case of doubt can comments area ask your questions or background, we communicate together, grow together.

Good guys, if the article is still useful to you, please help follow , share to your circle of friends , share technology, share happiness

Technology is open, and our mindset should be more open. Embrace the change, live toward the sun, and work hard to move forward.

Author: little devil child Rebels


阿兵云原生
192 声望37 粉丝