go的init函数中禁止使用:=声明新变量,必须用var显式声明或对已声明包级变量赋值;需注意初始化顺序、nil切片/map需make、重逻辑宜用sync.once惰性初始化。

init函数里不能用:=声明新变量
Go的init函数里不允许使用短变量声明操作符:=,因为init不是普通函数作用域,它不支持变量“声明并赋值”的语法糖。你只能用var显式声明,或对已声明的包级变量直接赋值。
常见错误现象:no new variables on left side of := —— 这说明你试图在init里用:=给一个还没声明过的标识符赋值。
- 正确做法:先在包顶层用
var声明变量,再在init中赋值 - 错误写法:
func init() { x := 42 }(报错) - 可接受写法:
var x int; func init() { x = 42 } - 如果变量依赖运行时逻辑(如读配置、连DB),必须用包级变量+
init赋值,不能靠const或初始化表达式
包级变量和init的执行顺序很关键
Go按源文件从上到下、文件按字典序初始化包级变量,然后才执行所有init函数。这意味着:变量声明时的初始化表达式(如var v = expensiveFunc())会在init之前求值;而init里的赋值一定发生在那之后。
- 想延迟计算?别在
var行直接调函数,把逻辑挪进init - 多个
init函数之间按源码顺序执行,但不同文件间顺序不确定(除非用构建标签约束) - 若变量A依赖变量B,且B需在
init中初始化,确保A的声明放在B之后,或A也通过init赋值
init中初始化切片、map、struct要小心零值
包级变量声明后默认是零值(nil切片、nil map、空struct),直接在init里用append或map[key] = val会panic。必须先make或&取地址。
-
var items []string; func init() { items = append(items, "a") }→ panic: append to nil slice - 正确:
var items []string; func init() { items = make([]string, 0); items = append(items, "a") } - map同理:
var cfg map[string]string; func init() { cfg = make(map[string]string); cfg["mode"] = "prod" } - struct字段如果是指针或接口,也要注意是否为nil,比如
http.Client字段需显式&http.Client{}
替代方案:用sync.Once + 惰性初始化更可控
如果初始化逻辑较重、或可能被多协程并发触发,init不是最佳选择——它只执行一次但无法控制时机,且无法返回错误。此时更适合用sync.Once封装惰性初始化函数。
-
init适合无副作用、必成功、纯内存的预热(如预建正则、初始化常量map) - 涉及I/O、网络、错误处理的场景,建议定义一个
func initConfig() error,配合sync.Once调用 - 例如:
var once sync.Once; var db *sql.DB; func getDB() (*sql.DB, error) { once.Do(func(){ /* open db */ }); return db, err } - 这样既避免
init失败导致程序退出,又支持按需加载和错误传播
真正容易被忽略的是:init函数没有参数、不能被显式调用,一旦写错逻辑(比如死锁、panic、循环依赖),整个包甚至主程序都无法启动,调试成本远高于普通函数。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











