Like many modern programming languages,
Go code is also organized as code packages.
To use the exported code elements (functions, types, variables and named constants,
etc) in a specified package, the package must first be imported,
except the builtin
standard code package (which is a universe package).
This article will explain code packages and package imports in Go.
simple-import-demo.go
.)
package main
import "fmt"
func main() {
fmt.Println("Go has", 25, "keywords.")
}
Some explanations:
simple-import-demo.go
.
The main
entry function of a program must be put in a package named main
.
fmt
standard package by using the import
keyword.
The identifier fmt
is the package name.
It is also used as the import name of, and represents, this standard package in the scope of containing source file.
(Import names will be explained a below section.)
There are many format functions declared in this standard package for other packages to use.
The Println
function is one of them.
It will print the string representations of
an arbitrary number of arguments to the standard output.
Println
function.
Note that the function name is prefixed with a fmt.
in the call,
where fmt
is the name of the package which contains the called function.
The form aImportName.AnExportedIdentifier
is called a
qualified identifier.
AnExportedIdentifier
is called an unqualified identifier.
fmt.Println
function call has no requirements
for its arguments, so in this program, its three arguments
will be deduced as values of their respective default types,
string
, int
and string
.
fmt.Println
call,
a space character is inserted between
each two consecutive string representations
and a newline character is printed at the end.
$ go run simple-import-demo.go
Go has 25 keywords.
Please note, only exported code elements in a package can be used
in the source file which imports the package.
An exported code element uses an
exported identifier as its name.
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 pseudo-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.
rand
is used as the import name
of the imported math/rand
standard package.
A rand.Uint32()
call will return
a random uint32
integer number.
Printf
is another commonly used function
in the fmt
standard package.
A call to the Printf
function must take at least one argument.
The first argument of a Printf
function call must be a string
value,
which specifies the format of the printed result.
The %v
in the first argument is called a format verb,
it will be replaced with the string representation of the second argument.
As we have learned in the article
basic types and their
literals, the \n
in a double-quoted
string literal will be escaped as a newline character.
Next pseudo-random number is 2596996162.
Note: before Go 1.20,
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 ()
.
package main
// Multiple packages can be imported together.
import (
"fmt"
"math/rand"
"time"
)
func main() {
// Set the random seed (only needed before Go 1.20).
rand.Seed(time.Now().UnixNano())
fmt.Printf("Next pseudo-random number is %v.\n", rand.Uint32())
}
Some explanations:
time
standard package,
which provides many time related utilities.
time.Now()
returns the current time,
as a value of type time.Time
.
UnixNano
is a method of the time.Time
type.
The method call aTime.UnixNano()
returns the number of nanoseconds
elapsed since January 1, 1970 UTC to the time denoted by aTime
.
The return result is a value of type int64
,
which is also the parameter type of the rand.Seed
function
(note: the rand.Seed
function has been deprecated since Go 1.20).
Methods are special functions. We can learn methods in the article
methods in Go for details later.
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.
%v
, which will be replaced with the general string representation of the corresponding argument.
%T
, which will be replaced with the type name or type literal of the corresponding argument.
%x
, which will be replaced with the hex string representation of the corresponding argument.
Note, the hex string representations for values of some kinds of types are not defined.
Generally, the corresponding arguments of %x
should be strings, integers,
integer arrays or integer slices (arrays and slices will be explained in a later article).
%s
, which will be replaced with the string representation of the corresponding argument.
The corresponding argument should be a string or byte slice.
%%
represents a percent sign.
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.
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 Go Toolchain, a package whose import path containing an internal
folder name
is viewed as a special package. It can only be imported by the packages
in and under 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.
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.
Later, we will call the packages named with main
and containing main
entry functions as program packages (or command packages),
and call other packages as library packages.
Program packages are not importable.
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
.
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.
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
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.
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.
import importname "path/to/package"
where importname
is optional,
its default value is the name (not the folder name) of the imported package.
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.
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 line fails to compile,
// for "rand" is not identified.
/*
fmt.Print("A random number: ", rand.Uint32(), "\n")
*/
}
Some explanations:
format
and random
as the
prefix token in qualified identifiers, instead of the real package names
fmt
and rand
.
Print
is another function in the fmt
standard package.
Like Println
function calls,
a Print
function call can take an arbitrary number of arguments.
It will print the string representations of the passed arguments, one by one.
If two consecutive arguments are both not string values,
then a space character will be automatically inserted between them in the print result.
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.
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 reduce code readability, so they 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 code elements 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).
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
}
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 Github. 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 @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.