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.
package main

import "fmt"

func main() {
	fmt.Println("Go has", 25, "keywords.")
}
Some Explanations: Assume the solo source file of this program is simple-import-demo.go, run this program 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 functionalites as the corresponding functions in the fmt standard package. Built-in functions can be used without importing any packages. However, the two built-in functions are not recommended to be used in production enviroments, 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 documentations.

A package import is also called an import declaration formally in Go. An import declaraton 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 pseudo-random number is always %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() {
	rand.Seed(time.Now().UnixNano()) // set the random seed.
	fmt.Printf("Next pseudo-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 mulitple 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.

Format verb %% represent a percent sign.

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.

About Package Folder Names

A code package may be composed of several source files. These source files are located in the same folder. Generally, a folder corresponds to one code package, and vice versa.

The official Go tools recommend, and often require, the folder containing a third-party package to be put in the src folder under any path specified in the GOPATH environment variable. The import path of the third-party package is the relative path of the package folder to the src folder. The seperators in the path must be always / and can't be \. For example, if the path of a package is OneGoPath/src/a/b/pkg (or OneGoPath\src\a\b\pkg on Windows), then its import path is /a/b/pkg.

A standard package has a higher import priority than a third-party package if their import paths are identical. So please try to avoid using the import path of a standard package as the import path of a third-party package.

Later, we will call the packages each of which contains the main entry functions as program packages, and call other packages as library packages. The names of program packages must be all main.

The name of folder containing a package is not required to be the same as the package name. However, for library packages, it will make package users confused if the name of a package is different from the name of the package folder. For example, if the name of a package is foo but its import path is x/y/bar, then to call a function F in the package, people may expect to use the qualified identifier bar.F. However, in fact the correct qualified identifier is foo.F. Package users must open the package source files to check what the real name of the imported package is. So please try to make the two names identical for every library package.

main packages are not recommended to be imported as library packages. Although Go specification doesn't forbid importing main packages, the official Go tools do (at least for Go SDK 1.10). In practice, to avoid confusing, it is best never to use main packages as library packages. One the other hand, it is recommended to give each program package folder a meaningful name other than its package name, main.

The official Go SDK will view package folders named as internal and vendor as special package folders.

Package Load Order

Go doesn't support circular dependency. If package a imports package b and package b imports package c, then package c can't import package a and b, package b also can't import package a.

A package also can't import itself.

At run time, packages are loaded by their dependency orders. A package will be loaded before all the packages which import it. Each Go program contains only one program package, which is the last package being loaded at run time.

The init Functions

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

The init functions in a package will be called once and only once sequentially when the package is loaded. However, the invocation order of these init functions is not specified in Go specification. So there shouldn't be dependency relations between the init functions in one package.

For the standard Go compiler, the init functions in a source file will be called from top to down.

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

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

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 respectfulName(who string) string {
	return "Mr. " + who
}

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

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

Full Package Import Form

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

where importname is optional, its default value is the real name of the imported package. In fact, the prefix token used in qualified identifiers is the importname.

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()) // set the random seed
	format.Print("A pseudo-random number: ", random.Uint32(), "\n")
	
	// The following two lines fail to compile.
	/*
	rand.Seed(time.Now().UnixNano()) // set the random seed
	fmt.Print("A pseudo-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 blank identifier (_). Such imports are called anonymous 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 Once

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

import "net/http"    // error: imported and not used
import . "time"      // error: imported and not used
import format "fmt"  // okay: used once below
import _ "math/rand" // okay: anonymous packages are 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 and code bugs.