preface

Go is a simple and interesting programming language, and like any other language, there are many pitfalls to use, but most of them are not design flaws in Go itself. If you’ve just switched to Go from another language, chances are you’ll step on the toes of this article.

If you take the time to learn about official doc, wiki, discussion mailing lists, Rob Pike’s numerous articles, and Go’s source code, you’ll find that the pits in this article are very common, and novice users can save a lot of time debugging code by skipping them.

Chapter 1:1-34

1. Open curly braces{Generally cannot put a single line

In most other languages, the position of {is up to you. Go is unique in that it follows the automatic semicolon injection rule: the compiler adds a certain separator at the end of each line of code. To separate multiple statements, for example by adding a semicolon after) :

// Error examples
func main(a)					
{
	println("hello world")}/ / equivalent
func main(a); // No function body					
{
	println("hello world")}Copy the code

./main.go: missing function body ./main.go: syntax error: unexpected semicolon or newline before {

// Correct example
func main(a) {
	println("hello world")}Copy the code

Note special cases such as code blocks:

// {if the semicolon injection rule is not followed, it will not be added automatically
func main(a){{println("hello world")}}Copy the code

Reference: Special separator for automatically adding semicolons in Golang

2. Unused variables

If there are unused variables in the function body code, it will not compile, but it is ok to declare global variables without using them.

Even if you assign a value to a variable after it is declared, it still won’t compile, so you need to use it somewhere:

// Error examples
var gvar int 	// Global variables can be declared without use

func main(a) {
	var one int 	// error: one declared and not used
	two := 2	// error: two declared and not used
	var three int	// error: three declared and not used
	three = 3		
}


// Correct example
// You can comment or remove unused variables directly
func main(a) {
	var one int
	_ = one
	
	two := 2
	println(two)
	
	var three int
	one = three

	var four int
	four = four
}
Copy the code

3. Unused import

If you import a package but none of the variables, functions, interfaces, and structures in the package are used, the compilation will fail.

You can avoid compilation errors by ignoring imported packages by using the _ _ _ symbol as an alias, which only executes the package’s init()

// Error examples
import (
	"fmt"	// imported and not used: "fmt"
	"log"	// imported and not used: "log"
	"time"	// imported and not used: "time"
)

func main(a){}// Correct example
// You can use the Goimports tool to comment or remove unused packages
import(_"fmt"
	"log"
	"time"
)

func main(a) {
	_ = log.Println
	_ = time.Now
}
Copy the code

4. Short declared variables can only be used inside functions

// Error examples
myvar := 1	// syntax error: non-declaration statement outside function body
func main(a){}// Correct example
var  myvar = 1
func main(a){}Copy the code

5. Repeat variables with short declarations

If there is at least one new variable to the left of :=, multiple variable declarations are allowed:

// Error examples
func main(a) {  
    one := 0
    one := 1 // error: no new variables on left side of :=
}


// Correct example
func main(a) {
	one := 0
	one, two := 1.2	// two is a new variable, allowing repeated declarations of one. For example, error processing often uses the same name variable err
	one, two = two, one	// Swap the shorthand for the values of the two variables
}
Copy the code

6. You cannot use a short declaration to set the value of a field

Struct variable fields cannot be assigned using := to use predefined variables to avoid resolution:

// Error examples
type info struct {
	result int
}

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

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


// Correct example
func main(a) {
	var data info
	var err error	// Err requires pre-declaration

	data.result, err = work()
	iferr ! =nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("info: %+v\n", data)
}
Copy the code

7. Accidentally overwriting variables

For developers moving from dynamic languages, short declarations are useful, which can lead to misunderstandings := is an assignment operator.

If you misuse a new block of code like :=, the compile will not report an error, but the variable will not work as you expect:

func main(a) {
	x := 1
	println(x)		/ / 1
	{
		println(x)	/ / 1
		x := 2
		println(x)	// the new x variable is scoped only within the code block
	}
	println(x)		/ / 1
}
Copy the code

This is a common mistake that Go developers make, and it’s not easy to catch.

You can use the VET tool to diagnose this variable override. Go does not do the override check 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
Copy the code

Note that VET does not report all covered variables, you can use GO-Nyet for further testing:

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

Variables of explicit type cannot be initialized using nil

Nil is the default initial value for variables of type interface, function, Pointer, Map, slice, and channel. However, the declaration does not specify a type, and the compiler cannot infer the specific type of the variable.

// Error examples
func main(a) {
    var x = nil	// error: use of untyped nil
	_ = x
}


// Correct example
func main(a) {
	var x interface{} = nil
	_ = x
}    
Copy the code

9. Use slice and Map with nil values

Adding elements to a nil slice is allowed, but adding elements to a nil map causes runtime panic

// Map error example
func main(a) {
    var m map[string]int
    m["one"] = 1		// error: panic: assignment to entry in nil map
    // m := make(map[string]int)// make(map[string]int
}    


// Correct example of slice
func main(a) {
	var s []int
	s = append(s, 1)}Copy the code

The capacity of the map

You can specify the size when creating a variable of type Map, but you can’t use cap() to detect the size of the allocated space as you can with slice:

// Error examples
func main(a) {
	m := make(map[string]int.99)
	println(cap(m)) 	// error: invalid argument m1 (type map[string]int) for cap  
}    
Copy the code

11. Variables of type string cannot be nil

For those of you who like to initialize strings in nil, this is a pit:

// Error examples
func main(a) {
	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"}}// Correct example
func main(a) {
	var s string	// The null value of the string type is an empty string ""
	if s == "" {
		s = "default"}}Copy the code

12. The value of the Array type is used as the function parameter

In C/C++, arrays (names) are Pointers. When you pass an array as an argument to a function, you pass a reference to the memory address of the array, which changes the value of the array inside the function.

In Go, arrays are values. When passed as an argument to a function, a copy of the original value of the array is passed, and the array cannot be updated inside the function:

// The array uses value copy to pass parameters
func main(a) {
	x := [3]int{1.2.3}

	func(arr [3]int) {
		arr[0] = 7
		fmt.Println(arr)	/ / [2, 3, 7]
	}(x)
	fmt.Println(x)			// [1 2 3] // It's not what you think.
}
Copy the code

If you want to modify the parameter array:

  • Pass the type of pointer to the array directly:
// The address changes the original data
func main(a) {
	x := [3]int{1.2.3}

	func(arr *[3]int) {
		(*arr)[0] = 7	
		fmt.Println(arr)	/ / & [2, 3, 7]
	}(&x)
	fmt.Println(x)	/ / [2, 3, 7]
}
Copy the code
  • Use slice directly: the original slice data (the underlying array) is updated even if the function internally obtains a copy of the slice value.
// Modify slice by modifying the array underlying slice
func main(a) {
	x := []int{1.2.3}
	func(arr []int) {
		arr[0] = 7
		fmt.Println(x)	/ / [2, 3, 7]
	}(x)
	fmt.Println(x)	/ / [2, 3, 7]
}
Copy the code

13. Range confused return values when traversing Slice and array

Unlike the for-in and foreach traversal statements in other programming languages, the range in Go generates two values during traversal, the first is the element index and the second is the element value:

// Error examples
func main(a) {
	x := []string{"a"."b"."c"}
	for v := range x {
		fmt.Println(v)	/ / 1 2 3}}// Correct example
func main(a) {
	x := []string{"a"."b"."c"}
	for _, v := range x {	// Use _ to discard the index
		fmt.Println(v)
	}
}
Copy the code

14. Slice and array are one-dimensional data

It looks like Go supports multidimensional arrays and slices, creating arrays of arrays and slices of slices, but it doesn’t.

For applications that rely on dynamically calculating multidimensional array values, Go is not ideal in terms of performance and complexity.

Dynamic multidimensional arrays can be created using original one-dimensional arrays, “independent” slices, and slices that “share the underlying array.”

  1. Use the original one-dimensional array: do index checking, overflow detection, and re-allocate memory when the array is full.

  2. Use “stand-alone” slices in two steps:

  • Creating an external Slice

    • Memory is allocated for each internal slice

      Note that the internal slices are independent of each other, so that any increase or decrease in an internal slice does not affect any other slice

// Create a dynamic multidimensional array for [2][3] using six separate slices
func main(a) {
	x := 2
	y := 4
	
	table := make([] []int, x)
	for i  := range table {
		table[i] = make([]int, y)
	}
}
Copy the code
  1. Use a slice of “Share underlying array”
  • Create a container slice to hold the raw data

  • Create another slice

  • Initialize other slices by cutting the original slice

func main(a) {
	h, w := 2.4
	raw := make([]int, h*w)

	for i := range raw {
		raw[i] = i
	}

	// Initialize raw slice
	fmt.Println(raw, &raw[4])	// [0 1 2 3 4 5 6 7] 0xc420012120 
    
	table := make([] []int, h)
	for i := range table {
        
        // Create a dynamic multidimensional array table by cutting the original slice with equal spacing
        // 0: raw[0*4: 0*4 + 4]
        // 1: raw[1*4: 1*4 + 4]
		table[i] = raw[i*w : i*w + w]
	}

	fmt.Println(table, &table[1] [0])	// [[0 1 2 3] [4 5 6 7]] 0xc420012120
}
Copy the code

More references on multidimensional arrays

go-how-is-two-dimensional-arrays-memory-representation

what-is-a-concise-way-to-create-a-2d-slice-in-go

15. Access the key that does not exist in the map

Like any other programming language, you want to return nil if you access a key that doesn’t exist in the map, such as in PHP:

> php -r '$v = ["x"=>1, "y"=>2]; @var_dump($v["z"]); '
NULL
Copy the code

Go returns the zero value of the corresponding data type of the element, such as nil, “”, false, and 0. The value operation always returns a value, so it is not possible to determine whether the key is in the map by retrieving the value.

To check whether the key is accessible directly from the map, check the second argument returned:

// Incorrect key detection mode
func main(a) {
	x := map[string]string{"one": "2"."two": ""."three": "3"}
	if v := x["two"]; v == "" {
		fmt.Println("key two is no entry")	// The two key returns an empty string if it does not exist}}// Correct example
func main(a) {
	x := map[string]string{"one": "2"."two": ""."three": "3"}
	if _, ok := x["two"]; ! ok { fmt.Println("key two is no entry")}}Copy the code

16. Values of type string are constants and cannot be changed

Attempts to use index traversal to update individual characters in the string are not allowed.

The value of string is a read-only binary byte slice. If you want to change the characters in the string, convert the string to []byte and then convert it to string:

// An example of an error in modifying a string
func main(a) {
	x := "text"
	x[0] = "T"		// error: cannot assign to x[0]
	fmt.Println(x)
}


// Modify the example
func main(a) {
	x := "text"
	xBytes := []byte(x)
	xBytes[0] = 'T'	// Note that T is of type rune
	x = string(xBytes)
	fmt.Println(x)	// Text
}
Copy the code

Note: The above example is not the correct way to update a string, because a UTF8 encoded character can be multiple bytes, such as a Chinese character that requires 3-4 bytes to store, and it is wrong to update one byte.

Correct posture for updating a string: convert a string into a Rune slice (1 Rune may be multiple bytes), directly updating the characters in the rune

func main(a) {
	x := "text"
	xRunes := []rune(x)
	xRunes[0] = '我'
	x = string(xRunes)
	fmt.Println(x)	/ / I am ext
}
Copy the code

17. Conversion between string and Byte slice

When a string and byte slice are converted to each other, the original value of the copy is converted. This conversion process is different from casting operations in other programming languages, and from the fact that the new slice shares the underlying array with the old slice.

Go optimizes the conversion between string and byte slice in two ways to avoid extra memory allocation:

  • inmap[string]Is used to find the key in[]byteTo avoid doingm[string(key)]Memory allocation for
  • usefor rangeIterating a string to an iteration of []byte:for i,v := range []byte(str) {... }

Fog: reference

18. String and index operators

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

This is handled in the same way as in other languages, such as PHP:

> php -r '$name = "Chinese"; var_dump($name); '	# "Chinese" takes 6 bytesString (6) "中文"
> php -r '$name = "Chinese"; var_dump($name[0]); ' # Reads the first byte as a Unicode character and displays U+FFFDString (1) "�"
> php -r '$name = "Chinese"; var_dump($name[0].$name[1].$name[2]); 'String (3) "in"Copy the code
func main(a) {
	x := "ascii"
	fmt.Println(x[0])		/ / 97
	fmt.Printf("%T\n", x[0])// uint8
}
Copy the code

If you need to use for range to iterate over characters in a string (Unicode Code point/rune), the library has the “Unicode/UTF8” package to do the related decoding for UTF8. Utf8string also has library functions like func (s *String) At(I int) rune.

19. Not all strings are UTF8 text

The value of string does not have to be UTF8 text and can contain arbitrary values. The string is UTF8 text only if it is a literal literal, and the string can be escaped to contain other data.

To determine if a string is UTF8 text, use the ValidString() function in the “Unicode/UTF8” package:

func main(a) {
	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))	// escape the escaped character to a literal
}
Copy the code

20. The length of the string

In Python:

data = U 'has'  
print(len(data)) # 1
Copy the code

In Go, however:

func main(a) {
	char := "Has"
	fmt.Println(len(char))	/ / 3
}
Copy the code

Go’s built-in function len() returns the number of bytes in the string, not Unicode characters as in Python.

To get the number of characters in the string, use RuneCountInString(STR string) (n int) in the “Unicode/UTF8” package

func main(a) {
	char := "Has"
	fmt.Println(utf8.RuneCountInString(char))	/ / 1
}
Copy the code

Note: RuneCountInString does not always return the number of characters we see, because some characters take up two rune:

func main(a) {
	char := "é"
	fmt.Println(len(char))	/ / 3
	fmt.Println(utf8.RuneCountInString(char))	/ / 2
	fmt.Println("cafe\u0301")	// Cafe // French cafe is actually a combination of two rune
}
Copy the code

Reference: normalization

21. The multi-line array, slice, and map statements are missing.

func main(a) {
	x := []int {
		1.2	// syntax error: unexpected newline, expecting comma or }
	}
	y := []int{1.2,}	
	z := []int{1.2}	
	// ...
}
Copy the code

Declaratory statements in which the} collapses into a single line, trailing, are not required.

22. log.Fatallog.PanicNot only the log

The log library provides different logging levels. Unlike other language logging libraries, the Go log package can do more than log, such as interrupt program execution, when calling Fatal*() and Panic*() :

func main(a) {
	log.Fatal("Fatal level log: log entry")		// After the output, the program terminates execution
	log.Println("Nomal level log: log entry")}Copy the code

23. Operations on built-in data structures are not synchronous

Although Go itself has a large number of features to support concurrency, it does not guarantee the security of concurrent data. Users need to ensure that data such as variables are updated by atomic operation.

Goroutine and channel are a good way to do atomic operations, or to use locks in the “sync” package.

24. Range Iterates the string value

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 can have more than one rune, such as the e in the French word cafe. You can manipulate special characters using the Norm package.

The for range iteration attempts to translate the string into UTF8 text, using the 0XFFFD RUNe (�) UNicode replacement character directly for any invalid code points. If there is any data in the string that is not UTF8, the string should be saved as a byte slice before operation.

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

	for _, v := range []byte(data) {
		fmt.Printf("%#x ", v)	0x41 0xFe 0x2 0xFF 0x4 // Correct}}Copy the code

25. Range Iterates the map

If you want to iterate over the map in a particular order (such as by key), be aware that each iteration may produce different results.

The Go runtime intentionally scrambles the iteration order, so you may get inconsistent iteration results. However, it is not always disrupted, and it is possible to get the same results of five consecutive iterations, such as:

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

If you run the above code repeatedly on the Go Playground, the output won’t change, and it will only recompile if you update the code. The iteration order is scrambled after recompilation:

26. Fallthrough statements in the switch

Case blocks in a switch statement are broken by default, but fallthrough can be used to force the next case block to execute.

func main(a) {
	isSpace := func(char byte) bool {
		switch char {
		case ' ':	// The space is broken directly, returning false // unlike other languages
		Fallthrough // Returns true
		case '\t':
			return true
		}
		return false
	}
	fmt.Println(isSpace('\t'))	// true
	fmt.Println(isSpace(' '))	// false
}
Copy the code

However, you can use fallthrough at the end of a case block to enforce the next case block.

Case:

func main(a) {
	isSpace := func(char byte) bool {
		switch char {
		case ' '.'\t':
			return true
		}
		return false
	}
	fmt.Println(isSpace('\t'))	// true
	fmt.Println(isSpace(' '))	// true
}
Copy the code

27. Increment and decrement operations

Many programming languages come with pre – and post – + and – operations. Go, however, is unique in that it does away with the preposition and uses ++ and – as operators rather than expressions.

// Error examples
func main(a) {
	data := []int{1.2.3}
	i := 0
	++i			// syntax error: unexpected ++, expecting }
	fmt.Println(data[i++])	// syntax error: unexpected ++, expecting :
}


// Correct example
func main(a) {
	data := []int{1.2.3}
	i := 0
	i++
	fmt.Println(data[i])	/ / 2
}
Copy the code

28. Reverse bitwise

Many programming languages use ~ as the unary bitwise invert (NOT) operator, and Go reuses the ^ XOR operator to bitwise invert:

// Incorrect fetch operation
func main(a) {
	fmt.Println(~2)		// bitwise complement operator is ^
}


// Correct example
func main(a) {
	var d uint8 = 2
	fmt.Printf("%08b\n", d)		/ / 00000010
	fmt.Printf("%08b\n", ^d)	/ / 11111101
}
Copy the code

^ is also the bitwise 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 AND NOT &^ operator that takes 1 for different bits.

func main(a) {
	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))
}
Copy the code
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)]Copy the code

29. Precedence of operators

In addition to the bit clear operator, Go has many of the same bit operators as other languages, but priority is another matter.

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

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

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

Priority list:

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

30. Unexported struct fields cannot be encode

Field members starting with a lowercase letter cannot be directly accessed externally, so struct will ignore these private fields during encode operations in JSON, XML, GOb and other formats, and will get zero value when exported:

func main(a) {
	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} // Private field two is ignored

	var out MyData
	json.Unmarshal(encoded, &out)
	fmt.Printf("%#v\n", out) 	// main.MyData{One:1, two:""}
}
Copy the code

31. The program exits with the Goroutine still running

By default, the program does not exit until all goroutines have been executed. This is important to note:

// The main program exits directly
func main(a) {
	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)		// Simulation goroutine is executing
	fmt.Printf("[%v] is done\n", workerID)
}
Copy the code

The main program exits before two Goroutines have finished executing:

Common solution: Use the “WaitGroup” variable, which makes the main program wait for all goroutines to execute before exiting.

If your Goroutine wants to do time-consuming things like loop processing of messages, send them a kill message to shut them down. Or just close a channel where they are both waiting to receive data:

// Wait for all goroutines to complete
// Enter the deadlock
func main(a) {
	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)
}
Copy the code

Execution Result:

It looks as if the goroutine has been executed, but an error is reported:

fatal error: all goroutines are asleep – deadlock!

Why do deadlocks occur? Goroutine called wg.done () before exiting, the program should exit normally.

The reason is that the “WaitGroup” variable goroutine gets is a copy of the var WG WaitGroup value, that is, doIt() passes only the value. So even if warg.done () is called in each goroutine, wg variables in the main program will not be affected.

// Wait for all goroutines to complete
// Pass parameters to the WaitGroup variable using the addressing mode
// Close the Goroutine with channel

func main(a) {
	var wg sync.WaitGroup
	done := make(chan struct{})
	ch := make(chan interface{})

	workerCount := 2
	for i := 0; i < workerCount; i++ {
		wg.Add(1)
        go doIt(i, ch, done, &wg)	// Wargaming passes Pointers, doIt() internally changes the wargaming value
	}

	for i := 0; i < workerCount; i++ {	// Send data to ch, close goroutine
		ch <- i
	}

	close(done)
	wg.Wait()
	close(ch)
	fmt.Println("all done!")}func doIt(workerID int, ch <-chan interface{}, done <-chan struct{}, wg *sync.WaitGroup) {
	fmt.Printf("[%v] is running\n", workerID)
	defer wg.Done()
	for {
		select {
		case m := <-ch:
			fmt.Printf("[%v] m => %v\n", workerID, m)
		case <-done:
			fmt.Printf("[%v] is done\n", workerID)
			return}}}Copy the code

Operation effect:

32. Send data to unbuffered channels and return as soon as the receiver is ready

The sender will only block if the data is being processed by the receiver. Depending on the runtime environment, the Receiver’s Goroutine may not have enough time to process the next piece of data after the sender has sent it. Such as:

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

	go func(a) {
		for m := range ch {
			fmt.Println("Processed:", m)
			time.Sleep(1 * time.Second)	// Simulate operations that need to run for a long time
		}
	}()

	ch <- "cmd.1"
	ch <- "cmd.2" // Will not be processed
}
Copy the code

Operation effect:

33. Sending data to a closed channel will cause a panic

It is safe to receive data from a closed channel:

When ok is false, there is no data available to receive in the channel. Similarly, when receiving data from a buffered channel, the status value is also false when the cached data has been retrieved and no more data is available

Sending data to a closed channel causes a panic:

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

	fmt.Println(<-ch)		// Output the first value sent
	close(ch)			// Can't close, there are other sender
	time.Sleep(2 * time.Second)	// Simulate to do other operations
}
Copy the code

Running result:

For the buggy example above, you can use a discarded channel done to tell the remaining Goroutines that they no longer need to send data to the CH. < -done = {};

func main(a) {
	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, "Send result")
			case <-done:
				fmt.Println(idx, "Exiting")
			}
		}(i)
	}

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

Operation effect:

34. The value ofnilThe channel

Sending and receiving data on a nil channel will permanently block:

func main(a) {
	var ch chan int // Uninitialized, value nil
	for i := 0; i < 3; i++ {
		go func(i int) {
			ch <- i
		}(i)
	}

	fmt.Println("Result: ", <-ch)
	time.Sleep(2 * time.Second)
}
Copy the code

Runtime deadlock error:

fatal error: all goroutines are asleep – deadlock! goroutine 1 [chan receive (nil chan)]

This deadlock feature can be used to dynamically open and close case blocks in select:

func main(a) {
	inCh := make(chan int)
	outCh := make(chan int)

	go func(a) {
		var in <-chan int = inCh
		var out chan<- int
		var val int

		for {
			select {
			case out <- val:
				println("-- -- -- -- -- -- -- --")
				out = nil
				in = inCh
			case val = <-in:
				println("+ + + + + + + + + +")
				out = outCh
				in = nil}}} ()go func(a) {
		for r := range outCh {
			fmt.Println("Result: ", r)
		}
	}()

	time.Sleep(0)
	inCh <- 1
	inCh <- 2
	time.Sleep(3 * time.Second)
}
Copy the code

Operation effect:

34. If the parameter passing mode of function receiver is value passing, the original value of the parameter cannot be modified

The parameters of the method Receiver are similar to the parameters of a normal function: if declared as a value, the method body gets a copy of the value of the parameter, and any changes to the parameter have no effect on the original value.

Unless the receiver parameter is a map or slice variable and is a pointer to a field in map or an element in slice, the value will be updated:

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

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

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

func main(a) {
	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()	// Change the num value to 7
	fmt.Printf("num=%v key=%v items=%v\n", d.num, *d.key, d.items)

	d.valueFunc()	// Modify the key and items values
	fmt.Printf("num=%v key=%v items=%v\n", d.num, *d.key, d.items)
}
Copy the code

Running result:

Intermediate: 35-50

35. Close the HTTP response body

When you make a request and get a response using the HTTP standard library, you need to manually close the response body, even if you don’t read any data from the response or the response is empty. It’s easy for beginners to forget to turn it off manually, or write it in the wrong place:

// The request failed, causing a panic
func main(a) {
	resp, err := http.Get("https://api.ipify.org?format=json")
	defer resp.Body.Close()	// Resp may be nil and cannot read the Body
	iferr ! =nil {
		fmt.Println(err)
		return
	}

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

	fmt.Println(string(body))
}

func checkError(err error) {
	iferr ! =nil{
		log.Fatalln(err)
	}
}
Copy the code

The above code makes the request correctly, but if the request fails, the variable resp is nil, causing a panic:

panic: runtime error: invalid memory address or nil pointer dereference

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

// A correct example in most cases
func main(a) {
	resp, err := http.Get("https://api.ipify.org?format=json")
	checkError(err)
    
	defer resp.Body.Close()	// The correct way to close in most cases
	body, err := ioutil.ReadAll(resp.Body)
	checkError(err)

	fmt.Println(string(body))
}
Copy the code

Output:

Get api.ipify.org?format=json: x509: certificate signed by unknown authority

In most cases of request failure, resP is nil and err is non-nil. But if you get a redirection error, and both of them are non-nil, you can still end up with a memory leak. Two solutions:

  • You can close the non-nil response body directly in the code block that handles HTTP response errors.
  • Manual calldeferTo close the response body:
// Correct example
func main(a) {
	resp, err := http.Get("http://www.baidu.com")
	
    // Close the correct posture for resp.body
    ifresp ! =nil {
		defer resp.Body.Close()
	}

	checkError(err)

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

	fmt.Println(string(body))
}
Copy the code

Earlier versions of resp.body.close () were implemented to read the response Body and then discard it, ensuring that keep-alive HTTP connections can reuse more than one request. However, the latest version of Go leaves the task of reading and discarding data to the user. If you don’t handle this, HTTP connections can be closed rather than reused, as described in the Go version 1.5 documentation.

If your program reuses a lot of HTTP persistent connections, you might want to include in the logic that handles the response:

_, err = io.Copy(ioutil.Discard, resp.Body)	// Manually discard the read data
Copy the code

The above code needs to be written if you need to read the response in its entirety. For example, in decoding API JSON response data:

json.NewDecoder(resp.Body).Decode(&data)  
Copy the code

36. Close the HTTP connection

Some servers that support HTTP1.1 or HTTP1.0 and have the Connection: keep-alive option configured maintain a long connection for a period of time. By default, however, the library “NET/HTTP” connections are only disconnected when the server asks to be shut down, so your program may run out of socket descriptors. There are two solutions, after the request is completed:

  • Set the request variable directlyCloseThe field values fortrueThe connection will be closed after each request.
  • Set Header request Header optionsConnection: closeThen the response header returned by the server will also have this option, and the HTTP library will actively disconnect.
// Close the connection voluntarily
func main(a) {
	req, err := http.NewRequest("GET"."http://golang.org".nil)
	checkError(err)

	req.Close = true
	// req.header. Add("Connection", "close") // Equivalent close mode

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

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

	fmt.Println(string(body))
}
Copy the code

You can create a custom configured HTTP Transport client to unreuse HTTP global connections:

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

	resp, err := client.Get("https://golang.google.cn/")
	ifresp ! =nil {
		defer resp.Body.Close()
	}
	checkError(err)

	fmt.Println(resp.StatusCode)	/ / 200

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

	fmt.Println(len(string(body)))
}
Copy the code

Select the following scenarios as required:

  • If your application sends a large number of requests to the same server, use the default keep-alive connection.

  • If your application is connecting to a large number of servers and each server requests only once or twice, close the connection when you receive the request. Or increase the value of the maximum number of open files fs.file-max.

37. Decode the numbers in JSON to the interface type

When encode/decode JSON data, Go defaults to treating a value as a float64 value. For example, the following code will cause panic:

func main(a) {
	var data = []byte(`{"status": 200}`)
	var result map[string]interface{}

	iferr := json.Unmarshal(data, &result); err ! =nil {
		log.Fatalln(err)
	}

	fmt.Printf("%T\n", result["status"])	// float64
	var status = result["status"]. (int)	// Type assertion error
	fmt.Println("Status value: ", status)
}
Copy the code

panic: interface conversion: interface {} is float64, not int

If you try to decode a JSON field with an integer, you can:

  • Convert int value to float

  • Convert the float value required after decode to an int

// Convert the decode value to int
func main(a) {
    var data = []byte(`{"status": 200}`)
    var result map[string]interface{}

    iferr := json.Unmarshal(data, &result); err ! =nil {
        log.Fatalln(err)
    }

    var status = uint64(result["status"]. (float64))
    fmt.Println("Status value: ", status)
}
Copy the code
  • useDecoderType to decode JSON data, explicitly indicating the value type of the field
// Specify the field type
func main(a) {
	var data = []byte(`{"status": 200}`)
	var result map[string]interface{}
    
	var decoder = json.NewDecoder(bytes.NewReader(data))
	decoder.UseNumber()

	iferr := decoder.Decode(&result); err ! =nil {
		log.Fatalln(err)
	}

	var status, _ = result["status"].(json.Number).Int64()
	fmt.Println("Status value: ", status)
}

 // You can use string to store numeric data, and decide whether to use int or float at decode
 // Convert the data to string by decode
 func main(a) {
 	var data = []byte({"status": 200})
  	var result map[string]interface{}
  	var decoder = json.NewDecoder(bytes.NewReader(data))
  	decoder.UseNumber()
  	iferr := decoder.Decode(&result); err ! =nil {
  		log.Fatalln(err)
  	}
    var status uint64
  	err := json.Unmarshal([]byte(result["status"].(json.Number).String()), &status);
	checkError(err)
   	fmt.Println("Status value: ", status)
}
Copy the code

– Use the struct type to map the data you need to numeric

// Struct specifies the field type
func main(a) {
  	var data = []byte(`{"status": 200}`)
  	var result struct {
  		Status uint64 `json:"status"`
  	}

  	err := json.NewDecoder(bytes.NewReader(data)).Decode(&result)
  	checkError(err)
	fmt.Printf("Result: %+v", result)
}
Copy the code
  • You can use struct to map numeric types to json.RawMessage native data types

    If the JSON data is not worried by decode or the value type of a JSON field is not fixed, etc.

// The status name can be int or string and is specified as json.rawMessage
func main(a) {
	records := [][]byte{[]byte(`{"status":200, "tag":"one"}`),
		[]byte(`{"status":"ok", "tag":"two"}`),}for idx, record := range records {
		var result struct {
			StatusCode uint64
			StatusName string
			Status     json.RawMessage `json:"status"`
			Tag        string          `json:"tag"`
		}

		err := json.NewDecoder(bytes.NewReader(record)).Decode(&result)
		checkError(err)

		var name string
		err = json.Unmarshal(result.Status, &name)
		if err == nil {
			result.StatusName = name
		}

		var code uint64
		err = json.Unmarshal(result.Status, &code)
		if err == nil {
			result.StatusCode = code
		}

		fmt.Printf("[%v] result => %+v\n", idx, result)
	}
}
Copy the code

Struct, array, slice, map

We can compare struct variables using the equality operator == if the members of both structs are of comparable type:

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(a) {
	v1 := data{}
	v2 := data{}
	fmt.Println("v1 == v2: ", v1 == v2)	// true
}
Copy the code

If any of the members of two constructs are not comparable, a compilation error will occur. Note that array members are only comparable if the array elements are comparable.

type data struct {
	num    int
	checks [10]func(a) bool// No comparisondoIt   func(a) bool// No comparisonm      map[string]string// No comparisonbytes  []byte// Cannot compare}func main(a) {
	v1 := data{}
	v2 := data{}

	fmt.Println("v1 == v2: ", v1 == v2)
}
Copy the code

invalid operation: v1 == v2 (struct containing [10]func() bool cannot be compared)

Go provides library functions to compare variables that can’t be compared using ==, such as DeepEqual() using the “reflect” package:

// Compare elements that cannot be compared by the equality operator
func main(a) {
	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}
   	// Note that the two slices are equal. The values and order must be the same
	fmt.Println("v1 == v2: ", reflect.DeepEqual(s1, s2))	// true
}
Copy the code

This comparison may be slow, depending on your program’s needs. DeepEqual() has other uses:

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

Note:

  • DeepEqual()Not always suitable for comparing slices
func main(a) {
	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
}
Copy the code

To compare English text in byte or string case insensitive, use the ToUpper() and ToLower() functions of the “bytes” or “strings” package. To compare bytes or strings from other languages, use bytes.equalfold () and strings.equalfold ().

If byte slice contains data to authenticate the user (ciphertext hash, token, etc.), reflect.deepequal (), bytes.equal (), and bytes.Compare() should not be used. This three functions easy to program timing attacks, at this time you should use “crypto/subtle” is subtle in the package. ConstantTimeCompare () function

  • reflect.DeepEqual()Empty slice is not equal to nil slice, but be carefulbyte.Equal()They would be considered equal:
func main(a) {
	var b1 []byte = nil
	b2 := []byte{}

    // B1 and B2 are of the same length and have the same byte order
    // Nil is the same as slice in bytes
    fmt.Println("b1 == b2: ", bytes.Equal(b1, b2))	// true
}
Copy the code

39. Recover from panic

Call recover() in a deferred function, and it catches/interrupts panic

// An example of an incorrect recover call
func main(a) {
	recover(a)// Catch nothing
	panic("not good")	// A panic occurs and the main program exits
	recover(a)// Will not be executed
	println("ok")}// Example of correct recover call
func main(a) {
	defer func(a) {
		fmt.Println("recovered: ".recover() ()}panic("not good")}Copy the code

As you can see above, recover() only takes effect when called in the function that defer executes.

// An example of an incorrect call
func main(a) {
	defer func(a) {
		doRecover()
	}()
	panic("not good")}func doRecover(a) {
	fmt.Println("recobered: ".recover()}Copy the code

recobered: panic: not good

40. Update elements by updating references as range iterates over Slice, array, and map

In a range iteration, the value is actually a copy of the element’s value. Updating the copy does not change the original element, that is, the address of the copied element is not the address of the original element:

func main(a) {
	data := []int{1.2.3}
	for _, v := range data {
		v *= 10		// The original elements in the data are not modified
	}
	fmt.Println("data: ", data)	// data: [1 2 3]
}
Copy the code

If you want to change the value of the original element, you should access it directly using the index:

func main(a) {
	data := []int{1.2.3}
	for i, v := range data {
		data[i] = v * 10	
	}
	fmt.Println("data: ", data)	// data: [10 20 30]
}
Copy the code

If your collection holds Pointers to values, modify them a bit. You still need to use the index to access the elements, but you can use the range element to update the original value directly:

func main(a) {
	data := []*struct{ num int} {{1}, {2}, {3}},for _, v := range data {
		v.num *= 10	// Use pointer update directly
	}
	fmt.Println(data[0], data[1], data[2])	/ / & {10} and {} 20 & {30}
}
Copy the code

41. Hidden data in slice

When a new slice is cut from a slice, the new slice references the underlying array of the original slice. If this hole is skipped, the program may allocate large amounts of temporary slice to point to parts of the original underlying array, resulting in unpredictable memory usage.

func get(a) []byte {
	raw := make([]byte.10000)
	fmt.Println(len(raw), cap(raw), &raw[0])	// 10000 10000 0xc420080000
	return raw[:3]	// Redistribute slice with a size of 10000
}

func main(a) {
	data := get()
	fmt.Println(len(data), cap(data), &data[0])	// 3 10000 0xc420080000
}
Copy the code

This can be solved by copying the data from a temporary slice, rather than re-slicing:

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

func main(a) {
	data := get()
	fmt.Println(len(data), cap(data), &data[0])	// 3 3 0xc4200160b8
}
Copy the code

Misuse of data in Slice

As a simple example, overwrite the file path (stored in Slice)

Split the path to point to each directory at a different level, change the first directory name and reorganize the subdirectory names to create a new path:

// Example of incorrect use of slice stitching
func main(a) {
	path := []byte("AAAA/BBBBBBBBB")
	sepIndex := bytes.IndexByte(path, '/') / / 4
	println(sepIndex)

	dir1 := path[:sepIndex]
	dir2 := path[sepIndex+1:]
	println("dir1: ".string(dir1))		// AAAA
	println("dir2: ".string(dir2))		// BBBBBBBBB

	dir1 = append(dir1, "suffix"...).println("current path: ".string(path))	// AAAAsuffixBBBB
    
	path = bytes.Join([][]byte{dir1, dir2}, []byte{'/'})
	println("dir1: ".string(dir1))		// AAAAsuffix
	println("dir2: ".string(dir2))		// uffixBBBB

	println("new path: ".string(path))	AAAAsuffix/uffixBBBB // Error result
}
Copy the code

AAAAsuffix/BBBBBBBBB = AAAAsuffix/BBBBBBB = AAAAsuffix/BBBBBBB = AAAAsuffix/BBBBBBB

Solution:

  • Reallocate the new slice and copy the data you need
  • Using a full slice expression:input[low:high:max], the capacity is adjusted to max-low
// Use full slice expression
func main(a) {

	path := []byte("AAAA/BBBBBBBBB")
	sepIndex := bytes.IndexByte(path, '/') / / 4
    dir1 := path[:sepIndex:sepIndex]		// Cap (dir1) is set to 4 instead of 16
	dir2 := path[sepIndex+1:]
	dir1 = append(dir1, "suffix"...). path = bytes.Join([][]byte{dir1, dir2}, []byte{'/'})
	println("dir1: ".string(dir1))		// AAAAsuffix
	println("dir2: ".string(dir2))		// BBBBBBBBB
	println("new path: ".string(path))	// AAAAsuffix/BBBBBBBBB
}
Copy the code

The third argument in line 6 is used to control the new capacity of dir1, and a new buffer will be allocated to hold any excess elements in dir1. Rather than overwriting the original path underlying array

43. The old slice

When you create a new slice from an existing slice, the data for both slices points to the same underlying array. If your program uses this feature, be aware of stale slice issues.

In some cases, when an element is appended to a slice and the underlying array it points to is insufficient, a new array will be allocated to store the data. Other slices also point to the old underlying array.

// If the capacity is exceeded, the array will be reallocated to copy the values and store them again
func main(a) {
	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
	}
	// In this case, s1 and s2 refer to the same underlying array
	fmt.Println(s1)		/ / 22 23 [1]
	fmt.Println(s2)		/ / [23] 22

	s2 = append(s2, 4)	// Append an element to s2 of size 2, and a new array is allocated to store it

	for i := range s2 {
		s2[i] += 10
	}
	fmt.Println(s1)		// [1 22 23] // At this point, s1 is no longer updated
	fmt.Println(s2)		/ / 32 33 [14]
}
Copy the code

44. Type declarations and methods

When creating a new type from an existing non-interface type, it does not inherit the original method:

// Define a custom type for Mutex
type myMutex sync.Mutex

func main(a) {
	var mtx myMutex
	mtx.Lock()
	mtx.UnLock()
}
Copy the code

mtx.Lock undefined (type myMutex has no field or method Lock)…

If you need to use the methods of the old type, you can embed the old type as an anonymous field in the new struct you define:

// Types are directly embedded in the form of fields
type myLocker struct {
	sync.Mutex
}

func main(a) {
	var locker myLocker
	locker.Lock()
	locker.Unlock()
}
Copy the code

The interface type declaration also preserves its set of methods:

type myLocker sync.Locker

func main(a) {
	var locker myLocker
	locker.Lock()
	locker.Unlock()
}
Copy the code

45. Jump out of the for-switch and for-select blocks

If you do not specify a label for a break, the switch/ SELECT statement will be displayed. If you cannot use a return statement, you can use a block of code for the break label:

// Break and label jump out of the specified code block
func main(a) {
loop:
	for {
		switch {
		case true:
			fmt.Println("breaking out...")
			// breaking out... // breaking out...
			break loop
		}
	}
	fmt.Println("out...")}Copy the code

Goto can also jump to a specified location, but it will still enter the for-switch again, in an infinite loop.

46. Iteration variables and closures in for statements

The iteration variables in the for statement are reused in each iteration, that is, the closure function created in the for always receives the same variable as an argument, and the same iteration value at the start of goroutine execution:

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

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

	time.Sleep(3 * time.Second)
	// Output three three three
}
Copy the code

The simplest solution: without modifying the goroutine function, use local variables inside for to store the iteration values and pass the arguments:

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

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

	time.Sleep(3 * time.Second)
	// Output one two three
}
Copy the code

Another solution: simply pass the current iteration value as a parameter to the anonymous function:

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

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

	time.Sleep(3 * time.Second)
	// Output one two three
}
Copy the code

Note the following three slightly more complex example differences:

type field struct {
	name string
}

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

// Error examples
func main(a) {
	data := []field{{"one"}, {"two"}, {"three"}}
	for _, v := range data {
		go v.print()
	}
	time.Sleep(3 * time.Second)
	// Output three three three
}


// Correct example
func main(a) {
	data := []field{{"one"}, {"two"}, {"three"}}
	for _, v := range data {
		v := v
		go v.print()
	}
	time.Sleep(3 * time.Second)
	// Output one two three
}

// Correct example
func main(a) {
	data := []*field{{"one"}, {"two"}, {"three"}}
	for _, v := range data {	// In this case, the iteration value v is the address of the three element values, each time v points to a different value
		go v.print()
	}
	time.Sleep(3 * time.Second)
	// Output one two three
}
Copy the code

47. The parameter values of the defer function

As of defer, its arguments are evaluated at declaration time, not execution time:

// In the defer function, the arguments are evaluated in advance
func main(a) {
	var i = 1
	defer fmt.Println("result: ".func(a) int { return i * 2 }())
	i++
}
Copy the code

result: 2

48. The execution time of the defer function

The function that executes as defer will be executed at the end of the function that called it, not at the end of the block that called it, be careful to distinguish between.

For example, in a long-executing function, use defer in the internal for loop to clear the resource calls generated by each iteration.

// The command-line argument specifies the directory name
// Iterate over the files in the read directory
func main(a) {

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

	dir := os.Args[1]
	start, err := os.Stat(dir)
	iferr ! =nil| |! start.IsDir() { os.Exit(2)}var targets []string
	filepath.Walk(dir, func(fPath string, fInfo os.FileInfo, err error) error {
		iferr ! =nil {
			return err
		}

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

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

	for _, target := range targets {
		f, err := os.Open(target)
		iferr ! =nil {
			fmt.Println("bad target:", target, "error:", err)	//error:too many open files
			break
		}
		defer f.Close()	// File resources are not closed at the end of each for block
		
		// Use the f resource}}Copy the code

Create 10000 files first:

#! /bin/bashfor n in {1.. 10000}; do echo content > "file${n}.txt" doneCopy the code

Operation effect:

Solution: write the deferred function to an anonymous function:

// Directory traversal is normal
func main(a) {
    // ...

	for _, target := range targets {
		func(a) {
			f, err := os.Open(target)
			iferr ! =nil {
				fmt.Println("bad target:", target, "error:", err)
				return	// Use return instead of break in an anonymous function
			}
			defer f.Close()	// When the anonymous function is finished, the call closes the file resource
			
			// Use the f resource(1)}}}Copy the code

Of course, you can remove defer and just call f.close () after the file resource has been used.

49. Failed type assertion

In a type assertion statement, a “zero value” of the target type is returned if the assertion fails, and an exception may occur if the assertion variable is mixed with the original variable:

// Error examples
func main(a) {
	var data interface{} = "great"

    / / data combination
	if data, ok := data.(int); ok {
		fmt.Println("[is an int], data: ", data)
	} else {
		fmt.Println("[not an int], data: ", data)	// [isn't a int], data: 0}}// Correct example
func main(a) {
	var data interface{} = "great"

	if res, ok := data.(int); ok {
		fmt.Println("[is an int], data: ", res)
	} else {
		fmt.Println("[not an int], data: ", data)	// [not an int], data: great}}Copy the code

50. Blocked Gorutinue and resource leak

At Google I/O 2012, Rob Pike’s Go Concurrency Patterns talk discussed some of Go’s basic Concurrency Patterns, such as a function that picks up the first data in a data set in complete code:

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

Each goroutine will send its search results to the results channel. The first data received in the channel will be returned directly.

What happens to the other goroutine results after the first data is returned? What about their own coroutines?

The resulting channel in First() is unbuffered, which means that only the First goroutine will return. Since there is no receiver, the other goroutines will block on the transmit. If you make a lot of calls, you may cause resource leaks.

To avoid leaks, you should make sure that all goroutines exit correctly. There are 2 solutions:

  • Use a buffered channel to ensure that all goroutines are received:
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
}
Copy the code
  • Use a select statement with a channel default statement that holds a cached value:

    The buffer channel of default guarantees that even if the result channel does not receive data, it will not block the Goroutine

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
}
Copy the code
  • Use the special Cancellation channel to interrupt the execution of the remaining Goroutine:
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
}
Copy the code

Rob Pike did not mention these problems in the presentation code in order to simplify the presentation. However, for beginners, it may be used without thinking.

Advanced: 51-57

51. Use a pointer as a method receiver

As long as the value is addressable, pointer methods can be called directly on the value. For a method, it is sufficient that its receiver is a pointer.

But not all values are addressable, such as elements of type Map, variables referenced through an interface:

type data struct {
	name string
}

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

func main(a) {
	d1 := data{"one"}
	d1.print(a)// The d1 variable is addressable and calls the pointer receiver's methods directly

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

	m := map[string]data{
		"x": data{"three"},
	}
	m["x"].print(a)// m["x"] is not addressable // changes frequently
}
Copy the code

cannot use data literal (type data) as type printer in assignment:

data does not implement printer (print method has pointer receiver)

cannot call pointer method on m[“x”] cannot take the address of m[“x”]

52. Update the value of the Map field

If the value of a map field is of the struct type, it is not possible to update a single field of that struct directly:

// Cannot directly update the struct field value
type data struct {
	name string
}

func main(a) {
	m := map[string]data{
		"x": {"Tom"},
	}
	m["x"].name = "Jerry"
}
Copy the code

cannot assign to struct field m[“x”].name in map

Because the elements in the map are not addressable. What is distinguishable is that elements of slice are addressable:

type data struct {
	name string
}

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

Note: A while ago the GCCGO compiler was able to update the field values of map struct elements, but this was soon fixed. It is officially considered a potential feature of Go1.3 and is still in the Todo list without timely implementation.

Update the field value of a struct element in a map.

  • Using local variables
// Extract the entire struct into a local variable, modify the field value and then assign the entire struct
type data struct {
	name string
}

func main(a) {
	m := map[string]data{
		"x": {"Tom"},
	}
	r := m["x"]
	r.name = "Jerry"
	m["x"] = r
	fmt.Println(m)	// map[x:{Jerry}]
}
Copy the code
  • Use a map pointer to an element
func main(a) {
	m := map[string]*data{
		"x": {"Tom"},
	}
	
	m["x"].name = "Jerry"	// Modify the fields in m["x"] directly
	fmt.Println(m["x"])	// &{Jerry}
}
Copy the code

But watch out for the following misuse:

func main(a) {
	m := map[string]*data{
		"x": {"Tom"},
	}
	m["z"].name = "what???"	 
	fmt.Println(m["x"])}Copy the code

panic: runtime error: invalid memory address or nil pointer dereference

53. Nil interface and nil interface values

Although interface looks like a pointer type, it is not. A variable of type interface is nil only if both type and value are nil

If the value of your interface variable changes with other variables (fog), be careful when comparing it to nil:

func main(a) {
	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)	// 
      
        false // The data value is nil but the in value is not nil
      
}
Copy the code

If your function returns a value of type interface, beware of this pit:

// Error examples
func main(a) {
	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: 
      
		fmt.Printf("%T\n", res)			// *struct {} // res is not nil
		fmt.Printf("%v\n", res)			// <nil>}}// Correct example
func main(a) {
	doIt := func(arg int) interface{} {
		var result *struct{} = nil
		if arg > 0 {
			result = &struct{}{}
		} else {
			return nil	// Return nil explicitly
		}
		return result
	}

	if res := doIt(- 1); res ! =nil {
		fmt.Println("Good result: ", res)
	} else {
		fmt.Println("Bad result: ", res)	// Bad result: 
      }}Copy the code

54. Stack variables

You don’t always know whether your variables are allocated to the heap or the stack.

In C++, variables created with new are always allocated to the heap, but in Go, even if new() and make() are used to create variables, the location of the variables is still managed by the Go compiler.

The Go compiler determines where to store a variable based on its size and the result of its “escape analysis”, so it can return exactly the address of the local variable, which is not possible in C/C++.

In go build or go run, add the -m parameter to accurately analyze the variable allocation position of the program:

55. Concurrency and Parallelism

With Go 1.4 and below, only one execution context/OS thread will be used, meaning that at most one Goroutine will be executing at any one time.

Go 1.5 sets the number of executable contexts to the number of logical CPU cores returned by Runtime.numCPU (). Whether this is consistent with the actual total number of logical CPU cores in the system depends on the number of CPU cores assigned to your program. This can be adjusted using the GOMAXPROCS environment variable or dynamically using runtime.gomaxprocs ().

Myth: GOMAXPROCS is the number of CPU cores that execute a Goroutine, see the documentation

The value of GOMAXPROCS can exceed the actual number of cpus, with a maximum of 256 in 1.5

func main(a) {
	fmt.Println(runtime.GOMAXPROCS(- 1))	/ / 4
	fmt.Println(runtime.NumCPU())	/ / 4
	runtime.GOMAXPROCS(20)
	fmt.Println(runtime.GOMAXPROCS(- 1))	/ / 20
	runtime.GOMAXPROCS(300)
	fmt.Println(runtime.GOMAXPROCS(- 1))	// Go 1.9.2 // 300
}
Copy the code

56. Reordering of read and write operations

Go may reorder the execution of some operations, which can guarantee that operations are executed sequentially in a single goroutine, but does not guarantee the execution order of multiple goroutines:

var _ = runtime.GOMAXPROCS(3)

var a, b int

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

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

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

func main(a) {
	go u1()	// Multiple goroutines are executed in varying order
	go u2()	
	go p()
	time.Sleep(1 * time.Second)
}
Copy the code

Operation effect:

If you want to keep multiple Goroutines executing in the same order as in your code, you can use the locking mechanism in a channel or sync package.

57. Prioritize scheduling

Your program might have one Goroutine that prevents other Goroutines from running at runtime, such as a for loop that doesn’t let the scheduler run:

func main(a) {
	done := false

	go func(a) {
		done = true} ()for! done { }println("done !")}Copy the code

The body of the for loop does not have to be empty, but there is a problem if the code does not trigger scheduler execution.

The scheduler executes after GC, Go declarations, blocking channels, blocking system calls, and locking operations. It also executes on non-inline function calls:

func main(a) {
	done := false

	go func(a) {
		done = true} ()for! done {println("not done !")	// Does not execute inline
	}

	println("done !")}Copy the code

We can parse inline functions called in the for block by adding the -m argument:

You can also manually start the scheduler using Gosched() in the Runtime package:

func main(a) {
	done := false

	go func(a) {
		done = true} ()for! done { runtime.Gosched() }println("done !")}Copy the code

Operation effect:

Reprinted fromgithub.com/wuYin/blog

This article is also published in the wechat public number [Trail Information], welcome to scan the code to follow!