init 和 defer

2026年9月6日

两个特殊机制:initmain 之前自动跑,defer 把清理工作留到函数返回前。


init 函数

init 不能手写调用,也不能当参数传来传去。没有参数、没有返回值。main 开始之前,运行时会按依赖顺序自动执行各个包的 init

package main

import "fmt"

func init() {
	fmt.Println("先 init")
}

func main() {
	fmt.Println("后 main")
}
go

同一个文件可以写多个 init写在前面的先跑。同一个包里多个文件的顺序,编译器按文件名排序后再跑 init

跨包时:先初始化被 import 的包,再初始化当前包。一个包只初始化一次,就算被 import 多次。

包级变量的初始化在该文件的 init 之前。大致顺序:

  1. 先把依赖包装好
  2. 再算本包的包级变量
  3. 再跑本包的 init
  4. 最后进 main

init 适合注册驱动、检查环境;别在里面做又慢又容易失败的网络请求,出错很难排查。


defer 函数

defer 登记一次调用,真正执行是在函数 return 之前。文件、锁、成对的「打开 / 关掉」都适合用它。

f, err := os.Open("a.txt")
if err != nil {
	return err
}
defer f.Close()
go

多个 defer 是栈:后登记的先执行,离 return 近的先跑。

func order() {
	defer fmt.Println(1)
	defer fmt.Println(2)
	fmt.Println(0)
}
// 打印:0  2  1
go

defer 后面那个调用,参数在登记时就算好。下面会打印 1,不是 2

n := 1
defer fmt.Println(n)
n = 2
go

如果要看「返回那一刻」的值,包一层闭包:

n := 1
defer func() { fmt.Println(n) }()
n = 2
// 打印 2
go

defer 能改命名返回值。和 recover 搭配可以接住 panic,异常那一课再写。


参考文档