1. Left curly braces are not allowed on a single line
2. Unused variables are not allowed
3. Unused imports are not allowed (imported using name)
4. Short Variable Declarations can only be used inside functions
// myvar := 1 // error
var myvar = 1 // ok
5. You cannot use short variable declarations (Short Variable Declarations) to repeat declarations
6. You cannot use Short Variable Declarations to set field values
data.result, err := work() //error
7. Accidental Variable Shadowing
The short variable declaration with the same name in the code block starts from the declaration to the end of the code block, and the modification of the variable will not affect the external variable!
8. You cannot use nil to initialize a variable of unspecified type
9. Slices and Maps that cannot directly use nil values
10. Capicity can be specified when map uses make to allocate memory, but cap function cannot be used for map
In golang, nil can only be assigned to variables of type pointer, channel, func, interface, map or slice.
12. When an array is used to pass parameters to a function, it is a value copy
Note: When a method or function is called, the incoming parameters are copied by value (consistent with assignment), unless special types such as map, slice, channel, and pointer type are passed by reference.
13. The range keyword returns a key-value pair, not a value
14. Slice and Array are one-dimensional
15. When taking a value from a map where there is no key, the returned value is always "0 value"
16. Strings are immutable
17. The conversion between string and []byte is copying (with memory loss), you can use map[string][]byte to create a mapping between string and []byte, or range to avoid memory allocation to improve performance
//[]byte:
for i,v := range []byte(str) {
}
18. The index operation of string returns byte (or uint8). If you want to get characters, you can use for range, or you can use the At() method of unicode/utf8 package and golang.org/x/exp/utf8string package.
19. Strings are not always UTF8 text
20. len(str) returns the number of bytes of the string
str := "我"
fmt.Println(len(str)) //3
21. When writing multiple lines of Slice, Array, and Map, the last comma cannot be omitted. When writing on a single line, the comma of the last element can be omitted.
22. The operations of built-in data structures are not synchronized, but the concurrency features provided by Go can be used: goroutines and channels.
23. Use for range to iterate String, which is iterated by rune.
A character can also be composed of multiple runes. Need to deal with characters, try to use the golang.org/x/text/unicode/norm package.
for range always tries to parse the string as utf8 text, and for bytes it can't parse, it returns oxfffd's rune characters.
Therefore, any text that contains non-utf8 must be converted to character slices ([]byte) first.
24. When using for range to iterate the map, the order of each iteration may be different, because the iteration of the map is random.
25. The case default matching rule of switch is different from other languages in that it exits by default after matching the case condition, unless you use fallthrough to continue matching; while other languages continue to match by default unless you use break to exit the matching.
26. Only post-increment ( a++
), post-decrement, no pre-increment ( ++a
), pre-decrement
27. The non-operation of bit operation is ^ (same as XOR bit operation), which is different from ~ in other languages.
28. The priority of bit operations (and, or, exclusive or, negation) is higher than that of four arithmetic operations (addition, subtraction, multiplication, division, remainder), which is different from the C language.
29. When the structure is serialized, the non-exported fields (field names starting with lowercase letters) will not be encoded, so the value of these non-exported fields is "0 value" when decoding
30. The program will exit without waiting for all goroutines to finish. The main goroutine (main goroutine) can be implemented through the channel to wait for all goroutines to complete.
31. For a channel without a buffer, the goroutine that writes to the channel will block until it is read, and the goroutine that reads the channel will block until data is written.
32. It is safe to read data from a channel in a closed state, and it can be judged whether it is closed by the return state (the second return parameter); and writing data to a channel in a closed state will cause panic.
33. Sending or reading data to a channel with a nil value (no space allocated by make) will cause forever blocking.
34. The receiver of the method is a type (T), and the receiver is only a copy of the value of the original object. Modifying the receiver in the method will not modify the value of the original object; if the receiver of the method is a pointer type (*T), it is the value of the original object. Of course, the modification of the method is the modification of the original object.
35. The log.Fatal and log.Panic in the log package not only record logs, but also terminate the program. It is different from Logging library.
36. Pay attention to the nil value when using the defer statement to close the resource, and perform the nil value judgment before the defer statement (otherwise it will cause a panic of null references)
37. Close the HTTP connection, you can use
- req.Close=true, which means to close the connection when the http request is completed
- Add Connection: close connection request header. The http server also sends a Connection: close response header, and the http library closes the connection when processing the response.
Globally close http connection reuse.
package main import ( "fmt" "net/http" "io/ioutil" ) func main() { //全局关闭http连接重用 //tr := &http.Transport{DisableKeepAlives: true} //client := &http.Client{Transport: tr} req, err := http.NewRequest("GET","http://golang.org",nil) if err != nil { fmt.Println(err) return } req.Close = true //or do this: //req.Header.Add("Connection", "close") resp, err := http.DefaultClient.Do(req) if resp != nil { defer resp.Body.Close() } if err != nil { fmt.Println(err) return } body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } fmt.Println(len(string(body))) }
37. Json deserializes numbers into values of type interface{}, and resolves to float64 by default
38. Comparison of Struct, Array, Slice, Map
If all fields of a struct structure can be compared using the == operation, then struct variables can also be compared using ==.
However, if struct fields cannot be compared using ==, then using == to compare struct variables will result in a compilation error.
Likewise, array variables can only be compared if each of its elements can be compared using ==.
Go provides some functions for comparison that cannot be compared directly using ==, the most commonly used is the reflect.DeepEqual() function.
The DeepEqual() function is not equal to a slice of nil values and a slice of empty elements, which is different from the bytes.Equal() function.
var b1 []byte = nil
b2 := []byte{}
fmt.Println("b1 == b2:",reflect.DeepEqual(b1, b2)) //prints: b1 == b2: false
var b3 []byte = nil
b4 := []byte{}
fmt.Println("b3 == b4:",bytes.Equal(b3, b4)) //prints: b3 == b4: true
To compare byte slices containing literal data, ignoring case,
It is not recommended to use the functions ToUpper() and ToLower() in the bytes package and the strings package and then compare them with ==, byte.Equal(), bytes.Compare(), etc. ToUpper() and ToLower() can only process English text, invalid for other languages. So it is recommended to use strings.EqualFold() and bytes.EqualFold()
If you want to compare byte slices used to verify user data key information, use reflact.DeepEqual(), bytes.Equal(),
bytes.Compare() exposes the application to timing attacks, you can use crypto/subtle.ConstantTimeCompare() to avoid leaking time information.
39. The recover() function can capture or intercept panic, but it must be called directly in the defer function or statement, otherwise it is invalid.
40. The data items obtained in the for range of slice, array, and map are copied from the elements of the collection, not referencing the original data, but the original data can be accessed by using the index.
data := []int{1,2,3}
for _,v := range data {
v *= 10 // original item is not changed
}
data2 := []int{1,2,3}
for i,v := range data2 {
data2[i] *= 10 // change original item
}
// 元素是指针类型就不一样了
data3 := []*struct{num int} {{1}, {2}, {3}}
for _,v := range data {
v.num *= 10
}
fmt.Println("data:", data) //prints data: [1 2 3]
fmt.Println("data:", data2) //prints data: [10 20 30]
fmt.Println(data3[0],data3[1],data3[2]) //prints &{10} &{20} &{30}
41. Generate another slice from a slice. The new slice will directly refer to the array of the original slice. The operations of the two slices on the same array will affect each other.
You can use copy() to reallocate space for the new slice, and copy part of the data from the slice to avoid mutual influence.
42. When continuing to slice from an existing slice, the capacity of the new slice is equal to the original capacity minus the number of parts before the new slice, and both the new slice and the original slice point to the same array space.
The capicity area between the newly generated slices overlaps, so it is easy to cause data coverage problems when adding data.
Slices reallocate space when content added using append exceeds capacity.
Taking advantage of this, specifying the capicity of the slice to be modified as the current length of the slice can avoid the effect of over-coverage between slices.
path := []byte("AAAA/BBBBBBBBB")
sepIndex := bytes.IndexByte(path,'/')
dir1 := path[:sepIndex]
// 解决方法
// dir1 := path[:sepIndex:sepIndex] //full slice expression
dir2 := path[sepIndex+1:]
fmt.Println("dir1 =>",string(dir1)) //prints: dir1 => AAAA
fmt.Println("dir2 =>",string(dir2)) //prints: dir2 => BBBBBBBBB
dir1 = append(dir1,"suffix"...)
path = bytes.Join([][]byte{dir1,dir2},[]byte{'/'})
fmt.Println("dir1 =>",string(dir1)) //prints: dir1 => AAAAsuffix
fmt.Println("dir2 =>",string(dir2)) //prints: dir2 => uffixBBBB (not ok)
fmt.Println("new path =>",string(path))
43. Before adding elements, slices share the same data area with other slices, and the modification will affect each other; but after adding elements to cause memory reallocation, it will no longer point to the original data area, and modifying elements will no longer affect other slices.
s1 := []int{1,2,3}
fmt.Println(len(s1),cap(s1),s1) //prints 3 3 [1 2 3]
s2 := s1[1:]
fmt.Println(len(s2),cap(s2),s2) //prints 2 2 [2 3]
for i := range s2 { s2[i] += 20 }
//still referencing the same array
fmt.Println(s1) //prints [1 22 23]
fmt.Println(s2) //prints [22 23]
s2 = append(s2,4)
for i := range s2 { s2[i] += 10 }
//s1 is now "stale"
fmt.Println(s1) //prints [1 22 23]
fmt.Println(s2) //prints [32 33 14]
44. Type redefinition and method inheritance
Redefining a new type from an existing (non-interface) non-interface type does not inherit any methods of the original type.
Inheritance of the anonymous variable type can be achieved by defining a type that composes the anonymous variable.
But when you redefine a new interface from an existing interface, the new interface will inherit all the methods of the original interface.
45. Jump out of "for switch" and "for select" code blocks.
A break without a label will only jump out of the innermost switch/select code block.
If you need to jump out of the outer for loop from the switch/select code block, you can define a label outside the for loop for break to jump out.
return is of course also possible, if it can be used here.
46. It is problematic to use iteration variables in the closure of a for statement
During the for iteration process, the iteration variable will always be retained, but the value of each iteration is different.
Therefore, in the for loop, the iteration variable is directly referenced in the closure, and the value of the iteration variable is directly taken during execution, not the variable value of the iteration where the closure is located.
If the closure wants to take the value of the iteration variable, you need to define a variable in for to save the value of the iteration, or pass parameters through the closure function.
47, defer function call parameters
The defer must be followed by a function or method call statement. Whether it is a function or a method after defer, the value of the input parameter is calculated when the defer is declared.
instead of calling start calculation.
It should be noted that when defer is followed by a method invocation statement, the receiver of the method is passed when the defer statement is executed, not when the defer statement is passed in.
48. The defer statement is called after the end of the current function, not the scope of the variable.
49. Failed type assertion: When the var.(T) type assertion fails, it will return the "0 value" of type T instead of the original value of the variable.
func main() {
var data interface{} = "great"
res, ok := data.(int);
fmt.Println("res =>",res, ",ok =>",ok)//res => 0 ,ok => false
}
50. Blocked goroutines and resource leaks
func First(query string, replicas ...Search) Result {
c := make(chan Result)
// 解决1:使用缓冲的channel: c := make(chan Result,len(replicas))
searchReplica := func(i int) { c <- replicas[i](query) }
// 解决2:使用select-default,防止阻塞
// searchReplica := func(i int) {
// select {
// case c <- replicas[i](query):
// default:
// }
// }
// 解决3:使用特殊的channel来中断原有工作
// 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
}
51. Call a method whose receiver is a pointer on a value instance
For addressable value variables (rather than pointers), methods that accept objects as pointer types can be called directly.
In other words, there is no need to define a method for an addressable value variable to accept an object as a value type.
However, not all variables are addressable, elements like Map are not.
package main
import "fmt"
type data struct {
name string
}
func (p *data) print() {
fmt.Println("name:",p.name)
}
type printer interface {
print()
}
func main() {
d1 := data{"one"}
d1.print() //ok
// var in printer = data{"two"} //error
var in printer = &data{"two"}
in.print()
m := map[string]data {"x":data{"three"}}
//m["x"].print() //error
d2 = m["x"]
d2.print() // ok
}
52. Update the field of the map value
If the value type of the map is a struct type, then the field values of the struct fetched from the map cannot be updated.
But it is possible for slices of struct type.
package main
type data struct {
name string
}
func main() {
m := map[string]data {"x":{"one"}}
//m["x"].name = "two" //error
r := m["x"]
r.name = "two"
m["x"] = r
fmt.Println(s) // prints: map[x:{two}]
mp := map[string]*data {"x": {"one"}}
mp["x"].name = "two" // ok
s := []data{{"one"}}
s[0].name = "two" // ok
fmt.Println(s) // prints: [{two}]
}
53. The interface{} of the nil value is not equal to nil
In golang, nil can only be assigned to variables of type pointer, channel, func, interface, map or slice.
interface{} represents any type and can receive values of any type. The interface{} variable is composed of two parts: type and value at the bottom, which is expressed as (T, V). The interface{} variable is special. When judging it is nil, its type and value are required to be nil, that is (nil, nil).
For other types of variables, as long as the value is nil, then this variable is nil (why? The variable type is not nil, of course, only the value can be used to judge)
Declare the variable interface{}, which is nil by default, and the underlying type and value representation is (nil, nil).
When a variable value V of any type T is assigned to an interface{} variable, the underlying representation of the interface{} variable is (T, V). As long as T is not nil, the interface{} variable is not nil even if V is nil.
var data *byte
var in interface{}
fmt.Println(data,data == nil) //prints: <nil> true
fmt.Println(in,in == nil) //prints: <nil> true
in = data
fmt.Println(in,in == nil) //prints: <nil> false
//'data' is 'nil', but 'in' is not 'nil'
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) //prints: good result: <nil>
//'res' is not 'nil', but its value is 'nil'
}
doit = func(arg int) interface{} {
var result *struct{} = nil
if(arg > 0) {
result = &struct{}{}
} else {
return nil //return an explicit 'nil'
}
return result
}
if res := doit(-1); res != nil {
fmt.Println("good result:",res)
} else {
fmt.Println("bad result (res is nil)") //here as expected
}
54. Allocation of variable memory
Using the new operator in C++ always allocates variables on the heap. Go compiler uses new() and make() to allocate memory location is stack or heap,
Depends on the size of the variable (size) and the result of "escape analysis". This means that in Go, there is no problem with returning references to local variables.
To know where the variable memory is allocated, you can specify -gcflags -m in the go build and go run commands:
go run -gcflags -m app.go
55. GOMAXPROCS, Concurrency and Parallelism
Go 1.4 and below use only one execution context per OS thread). This means that per time slice, only one goroutine executes.
Since Go 1.5, the number of execution contexts can be set to the number of CPU cores runtime.NumCPU(), or it can be set through the GOMAXPROCS environment variable,
It can also be set by calling the runtime.GOMAXPROCS() function.
Note that GOMAXPROCS does not represent the number of CPUs that the Go runtime can use, it is a small number of 256, which can be set to a larger number than the actual number of CPUs.
56. Sorting of read and write operations
Go may order some operations, but it guarantees that all behavior remains the same in the goroutine.
However, it cannot guarantee the order of execution across multiple goroutines.
package main
import (
"runtime"
"time"
)
var _ = runtime.GOMAXPROCS(3)
var a, b int
func u1() {
a = 1
b = 2
}
func u2() {
a = 3
b = 4
}
func p() {
println(a)
println(b)
}
func main() {
go u1()
go u2()
go p()
time.Sleep(1 * time.Second)
// 多次执行可显示以下以几种打印结果
// 1 2
// 3 4
// 0 2 (奇怪吗?)
// 0 0
// 1 4 (奇怪吗?)
}
57. Priority scheduling
There are some rogue goroutines that block the execution of other goroutines.
For example, a for loop may not allow the scheduler to execute.
The scheduler will be executed immediately after the GC, go statement, blocking channel operation, blocking system call, lock operation and other statements are executed.
You can also explicitly execute runtime.Gosched() (give up time slices) to make the scheduler perform scheduling work.
package main
import (
"fmt"
"runtime"
)
func main() {
done := false
go func(){
done = true
}()
for !done {
// ...
//runtime.Gosched() // 让scheduler执行调度,让出执行时间片
}
fmt.Println("done!")
}
Reference: https://blog.csdn.net/gezhonglei2007/article/details/52237582
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。