假设一个golang项目的三个源文件a.go,b.go, c.go,都定义了function inint(){}函数,

其中c.go文件初始化了一个全局变量globalVar,同时a.go 或者b.goinit func 引用了这个全局变量globalVar

那么这个时候就会出现一个问题,在a.gob.go的init func中 globalVar的引用是空值。

示例

文件结构

1
2
3
4
5
6
7
8
9
❯ tree
.
├── a.go
├── b.go
├── c.go
├── go.mod
└── main.go

0 directories, 5 files

源代码

 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

// file `a.go`
package main

import "fmt"

func init() {
        // globalVar is empty
        fmt.Println("globalVar in a.go:", globalVar)
}

// file `b.go`
package main

import "fmt"

func init() {
        // globalVar is empty
        fmt.Println("globalVar in b.go:", globalVar)
}


// file `c.go`
package main

import (
        "fmt"
        "time"
)

func init() {
        globalVar = initVar()
        fmt.Println("globalVar in c.go:", globalVar)
}

func initVar() string {
        time.Sleep(20 * time.Millisecond)
        return "late is better than never"
}

// file `main.go`
package main

import "fmt"

var globalVar = ""

func main() {
        fmt.Println("vim-go")
}

result

1
2
3
4
5
6
❯ go build -o demo
❯ ./demo 
globalVar in a.go: 
globalVar in b.go: 
globalVar in c.go: late is better than never
vim-go

解决办法

简单的解决办法可以是重命名c.go0a.go保证0a.go中的init最早执行完成,

1
mv c.go 01.go

这个解决方案的优点是可以不用修改代码,但是不够优雅。

比较好的解决方式应该是,不要在init func里初始化全局变量,应该直接在top block context中对全局变量初始化:

1
2
3
4
5
6
7
8
// file `c.go`
package main

var globalVar = initVar()

func init() {
    // ...
}