What's the purpose of golang allowing multiple init in one package?
This question may be somewhat opinion based, but using multiple package init()
functions can make your code easier to read and maintain.
If your source files are large, usually you arrange their content (e.g. types, variable declarations, methods etc.) in some logical order. Allowance of multiple init()
functions gives you the possibility to put initialization code near to the parts they ought to initialize. If this would not be allowed, you would be forced to use a single init()
function per package, and put everything in it, far from the variables they need to initialize.
Yes, having multiple init()
functions may require some care regarding the execution order, but know that using multiple init()
functions is not a requirement, it's just a possibility. And you can write init()
functions to not have "side" effects, to not rely on the completion of other init()
functions.
If that is unavoidable, you can create one "master" init()
which explicitly controls the order of other, "child" init()
functions.
An example of a "master" init()
controlling other initialization functions:
func init() {
initA()
initB()
}
func initA() {}
func initB() {}
In the above example, initA()
will always run before initB()
.
Relevant section from spec: Package initialization.
Also see related question: What does lexical file name order mean?
Another use case for multiple init()
functions is adding functionality based on build tags. The init()
function can be used to add hooks into the existing package and extend its functionality.
The following is a condensed example demonstrating the addition of more commands to a CLI utility based on build tags.
package main
import "github.com/spf13/cobra"
var (
rootCmd = &cobra.Command{Use: "foo", Short: "foo"}
)
func init() {
rootCmd.AddCommand(
&cobra.Command{Use: "CMD1", Short: "Command1"},
&cobra.Command{Use: "CMD2", Short: "Command2"},
)
}
func main() {
rootCmd.Execute()
}
The above is the "vanilla" version of the utility.
// +build debugcommands
package main
import "github.com/spf13/cobra"
func init() {
rootCmd.AddCommand(&cobra.Command{Use: "DEBUG-CMD1", Short: "Debug command1"})
}
The contents of the second file extends the standard command with additional commands that are mostly relevant during development.
Compiling using go build -tags debugcommands
will produce a binary with the added commands, while omitting the -tags
flag will produce a standard version.