I'm new to golang and I'm currently researching how packages and importing them works. I have a few questions about this import. I am reading this article (it has more than 7k likes so I guess it's accurate it's first what google gives me when I type golang packages
).
To explain what I don't understand, I will first write what the project structure looks like and what the contents of all files are.
pkg
├── mypkg
| └── mypkg.go
|
├── pkg1
| └── pkg1.go
|
└── pkg2
└── pkg2.go
go.mod
main.go
As you can see, I have module with 3 packages (4 including main). The contents of all packages are listed below.
Content of the pkg1.go:
package pkg1
import (
"fmt"
"mymod/pkg/mypkg"
)
func init() {
fmt.Println("Hello everyone from pkg1 init")
}
func HelloFromPkg1() {
fmt.Println("Hello from pk1")
mypkg.Print()
}
Content of the pkg2.go:
package pkg2
import (
"fmt"
"mymod/pkg/mypkg"
)
func init() {
fmt.Println("Hello everyone from pkg2 init")
}
func HelloFromPkg2() {
fmt.Println("Hello from pk2")
mypkg.Print()
}
Content of the mypkg:
package mypkg
import "fmt"
func init() {
fmt.Println("Hello everyone from mypkg init")
}
var prom = 10
func Print() {
fmt.Printf("address of prom inside mypkg is: %p\n", &prom)
}
Content of the main.go:
package main
import (
"fmt"
"mymod/pkg/pkg1"
"mymod/pkg/pkg2"
)
func init() {
fmt.Println("Hello everyone from main init")
}
func main() {
pkg1.HelloFromPkg1()
pkg2.HelloFromPkg2()
}
So, main.go
includes pkg1
and pkg2
and both pkg
and pkg2
include mypkg
. The article I refer to states the following (it's bolded):
The main thing to remember is, an imported package is initialized only once per package.
Having that in mind, I am expecting something like this to be the output of my program:
Hello everyone from mypkg init
Hello everyone from pkg1 init
Hello everyone from mypkg init
Hello everyone from pkg2 init
Hello everyone from main init
Hello from pk1
address of prom inside mypkg is: 0xfee360 (some address)
Hello from pk2
address of prom inside mypkg is: 0xf321a3 (another address)
My expectation is that the following steps will be followed:
- entered the main package (1)
- pkg1 package is initialized (2)
- mypkg package is initialized (2.1)
- all global variables inside mypkg is initialized -
prom
in my case (2.1.1) - init function of mypkg is called (2.1.2)
- all global variables inside mypkg is initialized -
- init function of pkg1 is called (2.2)
- mypkg package is initialized (2.1)
- pkg2 package is initialized (3)
- mypkg package is initialized (3.1)
- all global variables inside mypkg is initialized -
prom
in my case (3.1.1) - init function of mypkg is called (3.1.2)
- all global variables inside mypkg is initialized -
- init function of pkg2 is called (3.2)
- mypkg package is initialized (3.1)
- main package is initialized (4)
- init function of main is called (5)
- main function of main package is called (6)
Instead of it, I get the following output:
Hello everyone from mypkg init
Hello everyone from pkg1 init
Hello everyone from pkg2 init
Hello everyone from main init
Hello from pk1
address of prom inside mypkg is: 0x8fe360
Hello from pk2
address of prom inside mypkg is: 0x8fe360
It looks like mypkg is initialized only once during the first import?! Also, the address of the global variable prom
is the same in pkg1 and pkg2 (in this case it's 0x8fe360
).
So my questions are:
- Does the author of the article made a misstake? Imported packages are not initialized only once per package, but once per module?
- Does this mean that global variables from one package will always be the same throughout the module (same address) no matter how many times and from where the package is included? I mean they will only be initialized once during first import?
- Is there some way to make my "flow" work? By that I mean there is independent initialization of one package per each import? In my example this means that
mypkg
is initialized once inpkg1
and another time inpkg2
. - Does anyone have a good article on packages and modules in go if the one I read is not correct?
I know for some these questions are some basics about golang, but for me as a beginner this caused some misunderstandings. All the more so since the result of the program's work does not match what the author of the article that comes up first during a Google search wrote. Any help is welcome. All the best!
CodePudding user response:
The packages included in a program is a set that is the transitive closure of all imported packages starting from main
. That is:
- It is a set. Every imported package is included only once. That means, if you have a variable defined in a package, that variable only appears once.
- All imported packages, and the packages they import, recursively, are included in the final binary.
As for initialization: your steps are correct, except that mypkg
is initialized only once. There are not multiple copies of mypkg
in the binary.