go语言作用域踩坑

今天饭饭给我出了个题目,下面这段代码为什么报错,怎么改?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main
import (
"fmt"
)
type A struct {
s string
}
func main() {
var a *A
if check(a) {
a, err := generate()
fmt.Println(a.s, err)
}
fmt.Println(a.s)
}
func generate() (*A, error) {
return &A{s: "b"}, nil
}
func check(a *A) bool {
return true
}

运行一下,发现报错如下:

1
2
3
4
5
6
7
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1095520]
goroutine 1 [running]:
main.main()
/go_code/src/go_examples/go_scope.go:18 +0x280
exit status 2

18行,也就是

1
fmt.Println(a.s)

报错了,报错没法提供更多信息(go新手,老鸟可能能看出端倪),我们加一些打印

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
var a *A
fmt.Printf("%p\n", &a) // 1
if check(a) {
a, err := generate()
fmt.Printf("%p\n", &a) // 2
fmt.Println(a.s, err) // 3
}
fmt.Printf("%p\n", &a) // 4
fmt.Println(a.s)
}

结果如下:

1
2
3
4
0xc42000c028 // 1
0xc42000c038 // 2
b <nil> // 3
0xc42000c028 // 4

我们发现,2处的a竟然不是我们定义的(1处)a,发生了什么!

其实看到这里很多人可能都明白了,其实是a, err := generate()里面:=的问题,我们最初的设想是golang会定义新变量err,而a为初始定义的那个变量(1处)。但实际情况是,对于使用:=定义的变量,如果新变量与那个同名已定义变量 (这里就是1处的变量a)不在一个作用域中时,那么golang会重新定义这个变量,这就是导致这个问题的真凶。

怎么改呢,我们重写一下main函数:

1
2
3
4
5
6
7
8
9
10
func main() {
var a *A
var err error
if check(a) {
a, err = generate()
fmt.Println(a.s, err)
}
fmt.Println(a.s)
}

如此即可~

这个坑真的非常容易踩,而且不太好发现,感谢饭饭🙏