Package initialization takes place into init functions. In this section, we will see how to use them.
\n\nMySQL is an open-source database management system.
\nA database is “a collection of interrelated data stored together in one or more computerized file”
SQL is a query language (Structured Query Language) that MySQL understands
MySQL is an SQL database. It means that it will understand commands written in SQL.
MySQL is a program that you can install on a computer. With MySQL, you create a database, inject and retrieve data. To do those actions, you will need to use a program to issue MySQL commands. Those programs are often called “clients”. Those clients will contact the server. The server is the computer where MySQL has been installed and, therefore, where the data is stored.
\nMySQL provides a Command Line Interface (CLI) program.
MySQL workbench offers a graphical interface (GUI)
Various programming languages have developed modules/libraries to issue commands to MySQL databases. Sometimes those programs are packaged into extensions that we can install easily.
In the standard library of Go, you can find the package database/sql
that provides a generic interface around SQL databases, ie. database systems that can understand SQL.
The standard library does not offer specific programs to speak to a specific database management system. That’s why we need to use a “driver” to speak to MySQL.
\nA MySQL driver is a module built to interact with a MySQL database. In this section, we will study the open-source driver github.com/go-sql-driver/mysql
.
// https://github.com/go-sql-driver/mysql/blob/master/driver.go\npackage MySQL\n\nimport (\n //...\n "database/sql"\n //...\n)\n// ...\n\n\nfunc init() {\n sql.Register("mysql", &MySQLDriver{})\n}
\nThe project defines a function named init.
\nIt has no parameters
It has no results
The function calls the exported function Register
from the database/sql
standard library package
And here is the Register
function :
// Register makes a database driver available by the provided name.\n// If Register is called twice with the same name or if driver is nil,\n// it panics.\nfunc Register(name string, driver driver.Driver) {\n //...\n}
\nThanks to the comment, we understand that this function will make the driver available.
\n\nLet’s take a look at this example program using the driver.
\n// package-init/init-mysql/main.go\npackage main\n\nimport (\n "database/sql"\n "log"\n\n _ "github.com/go-sql-driver/mysql"\n)\n\nfunc main() {\n db, err := sql.Open("mysql", "user:password@/dbname")\n if err != nil {\n panic(err)\n }\n log.Println(db)\n //...\n}
\nIn the import block, we have to import statements
\nThe first one \"database/sql\"
which imports the standard library package.
And _ \"github.com/go-sql-driver/mysql\"
which is a blank import
Then in the main function, we call the function Open from \"database/sql\"
There is no trace of the init function in our main package... It’s therefore perfectly understandable because :
\nThe init
function from the driver is unexported.
Therefore it’s impossible to call it from another package.
How does Go register the driver if we do not call the init function?
\nOur program will call the init function.
It’s called because we imported the driver with a blank import statement.
When you import a package with a blank import statement, the package init function will be run.
\nNote that the author of the driver package could also remove the init function and specifically, instruct the user to call the Register function manually...
\n\nThe initialization of a package takes place in a specific order defined by the go specification :
\nThe imported packages are initialized
\nVariables are initialized
Init functions are run
Then the package itself is initialized
\nVariables are initialized
Init functions are run
Init functions are run sequentially. In other words, init functions are not run concurrently.
\n\nTo illustrate those rules, let’s take an example program. In figure 1 you can see the file structure of the program
\nWe have three packages: room
, invoice
, and currency
, along with a main.go file (and a go.mod)
Here is the go.mod file :
\nmodule maximilien-andile.com/packageInit/rules\n\ngo 1.13
\nAnd here is the main file :
\n// package-init/rules-illustration/main.go\npackage main\n\nimport (\n "fmt"\n\n "maximilien-andile.com/packageInit/rules/invoice"\n)\n\nfunc init() {\n fmt.Println("main")\n}\nfunc main() {\n fmt.Println("--program start--")\n invoice.Print()\n}
\nIn the main source file, we have added an init
function. Inside the main
function, we call the invoice package :
// package-init/rules-illustration/invoice/invoice.go\npackage invoice\n\nimport (\n "fmt"\n\n "maximilien-andile.com/packageInit/rules/currency"\n)\n\nfunc init() {\n fmt.Println("invoice init")\n}\n\nfunc Print() {\n fmt.Println("INVOICE Number X")\n fmt.Println(54, currency.EuroSymbol())\n}
\nThe package invoice
use a function from the currency
package. Here is the source of currency
package :
// package-init/rules-illustration/currency/currency.go\npackage currency\n\nimport "fmt"\n\nvar f = func() string {\n fmt.Println("variable f initialized")\n return "test"\n}()\n\nfunc init() {\n fmt.Println("currency init")\n}\n\nfunc EuroSymbol() string {\n return "EUR"\n}
\nThe variable f
seems a little bit strange. We assign to the variable the output result of an anonymous function (see chapter [chap:Anonymous-Functions-and]). This function returns the string \"test\"
. Before returning it displays \"variable f initialized\"
.
This function demonstrates when variables are initialized. They call fmt.Println
, so we will detect initialization in the program output.
Finally, this is the source file of the room
package :
// package-init/rules-illustration/room/room.go\npackage room\n\nimport "fmt"\n\nfunc init() {\n fmt.Println("room init")\n}
\nWe can observe that the expected behavior in the output result of our program. First, the imported packages are initialized then the main package is initialized.
\n\n$ go build main.go\n./main\nvariable f initialized\ncurrency init\ninvoice init\nmain\n--program start--\nINVOICE Number X\n54 EUR
\n\nThe sequence of a package initialization is always the same :
\nVariables are initialized
Then the init functions are run
What we can note in the log is that packages are initialized in a specific order (see figure 2)
\nThis initialization sequence is following the dependencies of our main package. It begins with the package at the end of the dependency graph, which is the package currency
(see figure 3.
Absolutely! A package can define more than one init function. The runtime will launch init functions sequentially.
\n\nLet’s see how to determine which variable will be initialized first. Here is a package with three variables and an init function :
\n// package-init/rules-illustration-advanced/invoice/invoice.go\npackage invoice\n\nimport (\n "fmt"\n)\n\nvar c = func() string {\n fmt.Println("variable c initialized", b)\n return "value of c"\n}()\n\nvar a = func() string {\n fmt.Println("variable a initialized")\n return "value of a"\n}()\n\nvar b = func() string {\n fmt.Println("variable b initialized", a)\n return "value of b"\n}()\n\nfunc init() {\n fmt.Println("invoice init", c)\n}\n\nfunc Print() {\n fmt.Println("INVOICE Number X")\n}
\nAnd here is our main function :
\n// package-init/rules-illustration-advanced/main.go\npackage main\n\nimport (\n "fmt"\n\n "maximilien-andile.com/packageInit/rules2/invoice"\n)\n\nfunc init() {\n fmt.Println("main")\n}\nfunc main() {\n fmt.Println("--program start--")\n invoice.Print()\n}
\nAfter compilation, we can run the program :
\n$ go build main.go\n$ ./main\nvariable first initialized\nvariable second initialized value of first\nvariable third initialized value of first\ninvoice init value of third\nmain\n--program start--\nINVOICE Number X
\nWe note that the variables are initialized in the order :
\na
b
c
The first variable initialized does not depend on anything (except package fmt). It is the first to be initialized. Then the variable b
(which depends on variable a) is initialized. When b
is initialized, we can initialize c
.
Variables are not initialized from top to bottom: here, a
is initialized first, but it is put in the second position in the source file.
When the runtime launches the initialization process for a package it will proceed by “cycle”. Each variable has an attribute “ready for initialization”. A variable is considered to be “ready” for initialization when :
\nIt is not yet initialized
It has no initialization expression OR Its initialization expression has no dependencies on uninitialized variables.
During the first cycle, the runtime will select: the earliest variable in declaration order AND ready for initialization.
When the first cycle is completed, the second cycle is launched: Go will select the variable that is earliest in declaration order AND ready for initialization.
The third cycle will do the same.
The fourth will also do the same...
etc...
This process ends when there is no more variable to initialize. You can vizualize on figure 4 the algorithm used :
\nAs shown on figure 4 some variables may still be uninitialized. In that case, the program is invalid.
\n\nWhat is the name of the function that will initialize a package? How many parameters this function have? How many results?
Initialization functions are mandatory in each package. True or False?
Is it possible to have four initialization functions in a package?
Initialization functions are called before variables initialization. True or False?
The runtime can run several initialization functions potentially at the same time. True or False?
If package A imports package B, which initialization functions are run first ?
What is the name of the function that will initialize a package? How many parameters this function have? How many results?
\nfunc init() {\n}
\nInitialization functions are mandatory in each package. True or False?
\nFalse
Init functions are optional
Is it possible to have four initialization functions in a package?
\nInitialization functions are called before variables initialization. True or False?
\nFalse.
Init functions are called after variable initialization.
The runtime can run several initialization functions potentially at the same time. True or False?
\nIf package A imports package B, which initialization functions are run first ?
\nInitialization of packages takes place in init
functions.
init
functions are not mandatory.
A package can declare several init
functions.
init
functions are called after variable initialization.
init
functions are run sequentially.
Previous
\n\t\t\t\t\t\t\t\t\tPackages and imports
\n\t\t\t\t\t\t\t\tNext
\n\t\t\t\t\t\t\t\t\tTypes
\n\t\t\t\t\t\t\t\t