Learn Go facts and details by participating in some quizzes. NEW!

Code Packages and Package Imports

Like many modern programming languages, Go code is also organized as code packages. To use the exported resources (functions, types, variables and named constants, etc) in a specified package, the package must first be imported, except the builtin standard code package. This article will explain code packages and package imports in Go.

Introduction of Package Import

Let's view a small program which imports a standard code package. (Assume the source code of this program is stored in a file named simple-import-demo.go.)
package main

import "fmt"

func main() {
	fmt.Println("Go has", 25, "keywords.")
}
Some explanations: Running this program, you will get the following output:
$ go run simple-import-demo.go
Go has 25 keywords.

Please note, only exported resources in a package can be used in the source file which imports the package. Exported resources are the resources whose names are exported identifiers. For example, the first character of the identifier Println is an upper case letter (so the Println function is exported), which is why the Println function declared in the fmt standard package can be used in the above example program.

The built-in functions, print and println, have similar functionalities as the corresponding functions in the fmt standard package. Built-in functions can be used without importing any packages.

Note, the two built-in functions, print and println, are not recommended to be used in the production environment, for they are not guaranteed to stay in the future Go versions.

All standard packages are listed here. We can also run a local server to view Go documentation.

A package import is also called an import declaration formally in Go. An import declaration is only visible to the source file which contains the import declaration. It is not visible to other source files in the same package.

Let's view another example:
package main

import "fmt"
import "math/rand"

func main() {
	fmt.Printf("Next random number is %v.\n", rand.Uint32())
}

This example imports one more standard package, the math/rand package, which is a sub-package of the math standard package. This package provides some functions to produce pseudo-random numbers.

Some explanations: The above program will always output:
Next pseudo-random number is always 2596996162.

If we expect the above program to produce a different random number at each run, we should set a different seed by calling the rand.Seed function when the program just starts.

If multiple packages are imported into a source file, we can group them in one import declaration by enclosing them in a ().

Example:
package main

// Multiple packages can be imported together.
import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	 // Set the random seed.
	rand.Seed(time.Now().UnixNano())
	fmt.Printf("Next random number is %v.\n", rand.Uint32())
}
Some explanations:

More About fmt.Printf Format Verbs

As the above has mentioned, if there is one format verb in the first argument of a fmt.Printf call, it will be replaced with the string representation of the second argument. In fact, there can be multiple format verbs in the first string argument. The second format verb will be replaced with the string representation of the third argument, and so on.

In Go 101, only the following listed format verbs will be used. An example:
package main

import "fmt"

func main() {
	a, b := 123, "Go"
	fmt.Printf("a == %v == 0x%x, b == %s\n", a, a, b)
	fmt.Printf("type of a: %T, type of b: %T\n", a, b)
	fmt.Printf("1%% 50%% 99%%\n")
}
Output:
a == 123 == 0x7b, b == Go
type of a: int, type of b: string
1% 50% 99%

For more Printf format verbs, please read the online fmt package documentation, or view the same documentation by running a local documentation server. We can also run go doc fmt to view the documentation of the fmt standard package, and run go doc fmt.Printf to view the documentation of the fmt.Printf function, in a terminal.

Package Folder, Package Import Path and Package Dependencies

A code package may consist of several source files. These source files are located in the same folder. The source files in a folder (not including subfolders) must belong to the same package. So, a folder corresponds to a code package, and vice versa. The folder containing the source files of a code package is called the folder of the package.

For the official Go SDK, a package whose import path containing an internal folder name is viewed as a special package. It can only be imported by the packages rooted as the direct parent directory of the internal folder. For example, package .../a/b/c/internal/d/e/f and .../a/b/c/internal can only be imported by the packages whose import paths have a .../a/b/c prefix.

Depending on different scenarios, a folder with name vendor might be also viewed as a special package folder. The following paragraphs will explain when this happens.

In Go SDK 1.11, a modules feature was introduced. A module can be viewed as a collection of packages which have a common root (a package tree). Each module is associated with an root import path and a semantic version. The major version should be contained in the root import path, execpt the v0 or v1 major versions. Modules with different root import paths are viewed as different modules.

Go SDK 1.11 also introduced a GO111MODULE environment variable. Its value can be auto, on and off. Up to now (Go SDK v1.13), its default value is auto. By context, different SDK versions interpret auto as either on or off by different rules. Please check the official wiki for details.

If a package is contained within a GOPATH/src directory, and the modules feature is off, then its import path is the relative path to either the GOPATH/src directory or the closest vendor folder which containing the package.

For example, when the modules feature is off, then for the following hierarchical directory structure, Note,
_ GOPATH
  |_ src
     |_ x
        |_ vendor
        |  |_ w
        |     |_ foo
        |        |_ foo.go    // package foo
        |_ y
        |  |_ vendor
        |  |  |_ w
        |  |     |_ foo
        |  |        |_ foo.go // package foo
        |  |_ y.go            // package y
        |_ z
        |  |_ z.go            // package z
        |_ x.go               // package x

When the modules feature is on, the root import path of a module is often (but not required to be) specified in a go.mod file which is directly contained in the root package folder of the module. We often use the root import path to identify the module. The root import path is the common prefix of all packages in the module.

Only the vendor folder directly under the root path of a module is viewed as a special folder.

For example, when the modules feature is on, then in the module identified with example.com/mypkg shown blow, Note, when the x.go, y.go or z.go files import a package with import path w/foo, the imported package is always the package with folder MyProject/vendor/w/foo.
_ MyProject
     |_ go.mod                // module example.com/mypkg
     |_ vendor
     |  |_ w
     |     |_ foo
     |        |_ foo.go       // package foo
     |_ x
        |_ y
        |  |_ vendor
        |  |  |_ w
        |  |     |_ foo
        |  |        |_ foo.go // package foo
        |  |_ y.go            // package y
        |_ z
        |  |_ z.go            // package z
        |_ x.go               // package x

When one source file in a package imports another package, we say the importing package depends on the imported package.

Go doesn't support circular package dependencies. If package a depends on package b and package b depends on package c, then source files in package c can't import package a and b, and source files in package b can't import package a.

Surely, source files in a package can't, and don't need to, import the package itself.

Similar to package dependencies, a module might also depend on some other modules. The direct module dependecies and their versions are often specified in the go.mod file of the module. Circular module dependencies are supported, though such scenarios are rare in practice.

Later, we will call the packages named with main and containing main entry functions as program packages, and call other packages as library packages. Each Go program should contain one and only one program package.

The name of the folder of a package is not required to be the same as the package name. However, for a library package, it will make package users confused if the name of the package is different from the name of the folder of the package. The cause of the confusion is that the default import path of a package is the name of the package but what is contained in the import path of the package is the folder name of the package. So please try to make the two names identical for each library package.

On the other hand, it is recommended to give each program package folder a meaningful name other than its package name, main.

The init Functions

There can be multiple functions named as init declared in a package, even in a source code file. The functions named as init must have not any input parameters and return results.

Note, at the top package-level block, the init identifier can only be used in function declarations. We can't declare package-level variable/constants/types which names are init.

At run time, each init function will be (sequentially) invoked once and only once (before invoking the main entry function). So the meaning of the init functions are much like the static initializer blocks in Java.

Here is a simple example which contains two init functions:
package main

import "fmt"

func init() {
	fmt.Println("hi,", bob)
}

func main() {
	fmt.Println("bye")
}

func init() {
	fmt.Println("hello,", smith)
}

func titledName(who string) string {
	return "Mr. " + who
}

var bob, smith = titledName("Bob"), titledName("Smith")
The output of this program:
hi, Mr. Bob
hello, Mr. Smith
bye

Resource Initialization Order

At run time, a package will be loaded after all its dependency packages. Each package will be loaded once and only once.

All init functions in all involved packages in a program will be invoked sequentially. An init function in an importing package will be invoked after all the init functions declared in the dependency packages of the importing package for sure. All init functions will be invoked before invoking the main entry function.

The invocation order of the init functions in the same source file is from top to bottom. Go specification recommends, but doesn't require, to invoke the init functions in different source files of the same package by the alphabetical order of filenames of their containing source files. So it is not a good idea to have dependency relations between two init functions in two different source files.

All package-level variables declared in a package are initialized before any init function declared in the same package is invoked.

Go runtime will try to initialize package-level variables in a package by their declaration order, but a package-level variable will be initialized after all of its depended variables for sure. For example, in the following code snippet, the initializations the four package-level variables happen in the order y, z, x, and w.
func f() int {
	return z + y
}

func g() int {
	return y/2
}

var (
	w       = x
	x, y, z = f(), 123, g()
)

About more detailed rule of the initialization order of package-level variables, please read the article expression evaluation order.

Full Package Import Forms

In fact, the full form of an import declaration is
import importname "path/to/package"

where importname is optional, its default value is the name (not the folder name) of the imported package.

In fact, in the above used import declarations, the importname portions are all omitted, for they are identical to the respective package names. These import declarations are equivalent to the following ones:
import fmt "fmt"        // <=> import "fmt"
import rand "math/rand" // <=> import "math/rand"
import time "time"      // <=> import "time"

If the importname portion presents in an import declaration, then the prefix tokens used in qualified identifiers must be importname instead of the name of the imported package.

The full import declaration form is not used widely. However, sometimes we must use it. For example, if a source file imports two packages with the same name, to avoid making compiler confused, we must use the full import form to set a custom importname for at least one package in the two.

Here is an example of using full import declaration forms.
package main

import (
	format "fmt"
	random "math/rand"
	"time"
)

func main() {
	random.Seed(time.Now().UnixNano())
	format.Print("A random number: ", random.Uint32(), "\n")

	// The following two lines fail to compile,
	// for "rand" is not identified.
	/*
	rand.Seed(time.Now().UnixNano())
	fmt.Print("A random number: ", rand.Uint32(), "\n")
	*/
}
Some explanations:

The importname in the full form import declaration can be a dot (.). Such imports are called dot imports. To use the exported elements in the packages being dot imported, the prefix part in qualified identifiers must be omitted.

Example:
package main

import (
	. "fmt"
	. "time"
)

func main() {
	Println("Current time:", Now())
}

In the above example, Println instead of fmt.Println, and Now instead of time.Now must be used.

Generally, dot imports are not recommended to be used in formal projects.

The importname in the full form import declaration can be the blank identifier (_). Such imports are called anonymous imports (some articles elsewhere also call them blank imports). The importing source files can't use the exported resources in anonymously imported packages. The purpose of anonymous imports is to initialize the imported packages (each of init functions in the anonymously imported packages will be called once).

In the following example, all init functions declared in the net/http/pprof standard package will be called before the main entry function is called.
package main

import _ "net/http/pprof"

func main() {
	... // do somethings
}

Each Non-Anonymous Import Must Be Used at Least Once

Except anonymous imports, other imports must be used at least once. For example, the following example fails to compile.
package main

import (
	"net/http" // error: imported and not used
	. "time"   // error: imported and not used
)

import (
	format "fmt"  // okay: it is used once below
	_ "math/rand" // okay: it is not required to be used
)

func main() {
	format.Println() // use the imported "fmt" package
}

The Go 101 project is hosted on both Github and Gitlab. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.

If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account: @go100and1.

The digital versions of this book are available at the following stores and forms: Tapir, the author of Go 101, has spent 3+ years on writing the Go 101 book and maintaining the go101.org website. New contents will continue being added to the book and the website from time to time. If you would like to, you can support the book and the website by making a donation through Paypal ($5, $9, $15, or $25) or buying Tapir a coffee (way 1 and way 2).

You can also support Go 101 by playing Tapir's games.
Cryptocurrency donations are welcome too:
Bitcoin: 1xucQbv5jujFPPwhyg395ri5yV71hx9g9
Ethereum: 0x5dc4aa2c2bbfaadae373dadcfca11b3358912212