Hello everyone, I am fried fish.
When programming with Go, you will always encounter all kinds of strange errors. Many friends at home and abroad have summarized them (see the reference for the reference link), and it feels like you can get together.
I always wanted to write before, thinking about the May Day holiday. Today, I will share with you the common coding errors in Go (1), hoping to help you.
Go common mistakes
1. nil map
question
A map is declared (defined) in the program, and then the data is written directly. The following code:
func main() {
var m map[string]string
m["煎鱼"] = "进脑子了"
}
Output result:
panic: assignment to entry in nil map
A panic will be thrown directly.
Solution
The solution is actually to declare and initialize. The standard way of writing in Go is to call the make
function. The following code:
func main() {
m := make(map[string]string)
m["煎鱼"] = "下班了"
}
This question is one of the easiest mistakes to step on when learning Go.
2. Null pointer reference
question
We often use structures in Go to declare a series of methods, which seem to be "classes" in object-oriented, and are very common in business code.
The following code:
type Point struct {
X, Y float64
}
func (p *Point) Abs() float64 {
return math.Sqrt(p.X*p.X + p.Y*p.Y)
}
func main() {
var p *Point
fmt.Println(p.Abs())
}
Does this program work properly? Normal calculation and output?
Output result:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10a3143]
goroutine 1 [running]:
main.(*Point).Abs(...)
/Users/eddycjy/awesomeProject/main.go:13
main.main()
/Users/eddycj/awesomeProject/main.go:18 +0x23
It just panics because of a null pointer reference.
Solution
If the variable p is a pointer, it must be initialized before it can be called. The following code:
func main() {
var p *Point = new(Point)
fmt.Println(p.Abs())
}
Or use the value object method to solve:
func main() {
var p Point // has zero value Point{X:0, Y:0}
fmt.Println(p.Abs())
}
3. Using a reference to a loop iterator variable
question
In Go, the loop iterator variable is a single variable that takes a different value on each loop iteration. This can lead to unexpected behavior if used incorrectly.
The following code:
func main() {
var out []*int
for i := 0; i < 3; i++ {
out = append(out, &i)
}
fmt.Println("Values:", *out[0], *out[1], *out[2])
fmt.Println("Addresses:", out[0], out[1], out[2])
}
What is the output. A bold guess is that the values are 1, 2, and 3, and the addresses are all different. Is it right?
Output result:
Values: 3 3 3
Addresses: 0x40e020 0x40e020 0x40e020
The values are all 3, and the addresses are all pointing to the same point.
Solution
One of the workarounds is to copy the loop variable into a new variable:
for i := 0; i < 3; i++ {
i := i // Copy i into a new variable.
out = append(out, &i)
}
Output result:
Values: 0 1 2
Addresses: 0x40e020 0x40e024 0x40e028
The reason is: on each iteration, we append the address of i to the out slice, but since it's the same variable, we're actually appending the same address, which ends up containing the last value assigned to i.
So you only need to make a copy and disconnect the two.
4. Using goroutine on loop iterator variable
question
When looping in Go, we often use goroutines to process data concurrently. The most classic is to combine closures to write business logic.
The following code:
values := []int{1, 2, 3, 4, 5}
for _, val := range values {
go func() {
fmt.Println(val)
}()
}
time.Sleep(time.Second)
But in actual operation, the above for loop may not achieve your expectations, you may want to output the values in the slice sequentially.
The output is:
5
5
4
5
5
You may see the last element printed for each iteration, and you may even find that the output is different each time...
If you remove the sleep code, you will find that the goroutine may not start executing at all, and the program ends.
Solution
This is actually a common problem with the use of closures, and the correct way to write the closure loop is:
values := []int{1, 2, 3, 4, 5}
for _, val := range values {
go func(val int) {
fmt.Println(val)
}(val)
}
By adding val as a parameter to the closure, the variable val is stored on the goroutine's stack each time through the loop to ensure that the value is correct when the final goroutine executes.
Of course, there is a hidden problem here. Everyone always thinks that the output is 1, 2, 3, 4, 5 in order. In fact, it is not, because the execution of goroutines is random, and the order cannot be guaranteed.
Note: It is often distorted in many Go interview questions, and it is easy to be confusing once it is complicated.
5. The array will not be changed
question
Slices and numbers are the most widely used data types in Go programs, but they often have some weird problems.
The following code:
func Foo(a [2]int) {
a[0] = 8
}
func main() {
a := [2]int{1, 2}
Foo(a)
fmt.Println(a)
}
What is the output knot. It's [8 2], right?
Output result:
[1 2]
Why is this, a loneliness has been modified in the function?
Solution
In fact, in Go, all function passing is by value. That is, when an array is passed to a function, the array is copied.
If you really need to pass into the function to modify, you can use slices instead.
The following code:
func Foo(a []int) {
if len(a) > 0 {
a[0] = 8
}
}
func main() {
a := []int{1, 2}
Foo(a)
fmt.Println(a)
}
Output result:
[8 2]
The reason is: the slice will not store any data, its underlying data will point to an underlying array. Therefore, when modifying the elements of a slice, the corresponding elements of its underlying array will be modified, and other slices that share the same underlying array will be modified together.
Do you think this is all right, solved? No. When the slice is expanded, the bottom layer of Go will re-apply for a new larger space, and there are scenarios in which it is separated from the original slice.
Therefore, it is still necessary to return the changed value in time, and it is better to uniformly process the metadata in the main process.
Summarize
In today's post, we started the first section of Common Coding Mistakes in Go, covering a total of 5 cases:
- nil Map.
- A reference to a null pointer.
- Use a reference to the loop iterator variable.
- Use a goroutine on the loop iterator variable.
- The array will not be changed.
These cases are very common and are easier to spot when looking at a single code. But once it's mixed into an application, it's harder to see in the cluttered code.
I wish you all to stop stepping on pits and less bugs after smoking.
The article is updated continuously, you can read it by searching on WeChat [Brain fried fish]. This article has been included in GitHub github.com/eddycjy/blog . If you are learning Go language, you can see the Go learning map and route . Welcome to Star to urge you to update.
Recommended reading
- Introduction to the Go language series: a preliminary exploration of the actual combat of the Go project
- Go Programming Journey: Deep Dive into Go Projects
- Go Language Design Philosophy: Understanding Go Why and Design Thinking
- Go Language Advanced Tour: Go deeper into the Go source code
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。