I know that golang allows multiple init in one package and even in one file. I am wondering why? For example, if a pkg has many files, we could write multiple init then we could get lost in where to we should put init, and we could be also confused about the init order if we have multiple init in one pkg. (I mean is this better? we can only have 1 init, then we can have some initXXX, then put them into init, it seems quite clean.) What's the advantage of doing this in code struct view?
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 give 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 main() {
rootCmd.AddCommand(
&cobra.Command{Use: "CMD1", Short: "Command1"},
&cobra.Command{Use: "CMD1", Short: "Command1"},
)
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.