尽可能避免使用init()。当init()是不可避免或可取的,代码应先尝试:
无论程序环境或调用如何,都要完全确定。
避免依赖于其他init()函数的顺序或副作用。虽然init()顺序是明确的,但代码可以更改,
因此init()函数之间的关系可能会使代码变得脆弱和容易出错。
避免访问或操作全局或环境状态,如机器信息、环境变量、工作目录、程序参数/输入等。
避免I/O,包括文件系统、网络和系统调用。
不能满足这些要求的代码可能属于要作为main()调用的一部分(或程序生命周期中的其他地方),
或者作为main()本身的一部分写入。特别是,打算由其他程序使用的库应该特别注意完全确定性,
而不是执行“init magic”
Bad | Good |
---|---|
type Foo struct { // ... } var _defaultFoo Foo func init() { _defaultFoo = Foo{ // ... } } |
var _defaultFoo = Foo{ // ... } // or, 为了更好的可测试性: var _defaultFoo = defaultFoo() func defaultFoo() Foo { return Foo{ // ... } } |
---|
type Config struct { // ... } var config Config func init() { // Bad: 基于当前目录 cwd, := os.Getwd() // Bad: I/O raw, _ := ioutil.ReadFile( path.Join(cwd, "config", "config.yaml"), ) yaml.Unmarshal(raw, &_config) } |
type Config struct { // ... } func loadConfig() Config { cwd, err := os.Getwd() // handle err raw, err := ioutil.ReadFile( path.Join(cwd, "config", "config.yaml"), ) // handle err var config Config yaml.Unmarshal(raw, &config) return config } |
考虑到上述情况,在某些情况下,init()可能更可取或是必要的,可能包括:
不能表示为单个赋值的复杂表达式。
可插入的钩子,如database/sql、编码类型注册表等。
对Google Cloud Functions和其他形式的确定性预计算的优化。