Utilizing Go packages
Go provides reusable blocks of code that can be imported into other code using packages. Packages in Go are synonymous with libraries or modules in other languages. Packages are the building blocks of Go programs that divide the content into understandable parts.
This section will cover how to declare and import a package. We will discuss how to deal with package name conflicts, explore rules around packages, and we will write our first main package.
Declaring a package
Go divides programs into packages, sometimes called modules or libraries in other languages. Packages live on a path, and the path is made to look like a path to a directory on a Unix-like filesystem.
All Go files in a directory must belong to the same package. The package is most commonly named the same as the directory it lives in.
Declaring a package happens at the top of the file, and should only be preceded by a comment. Declaring a package is as simple as the following:
// Package main is the entrance point for our binary. // The double slashes provides a comment until the end of the line. /* This is a comment that lasts until the closing star slash. */ package main
package main
is special. All other package names declare a package that must be imported into another package to be used. package main
will declare func main()
, which is the starting point for a binary to run.
All Go files in a directory must have the same package header (compiler-enforced). These files, for most practical purposes, act as if they are concatenated together.
Let's say you have a directory structure as follows:
mypackage/ file1.go file2.go
Then, file1.go
and file2.go
should have the following:
package mypackage
When mypackage
is imported by another package, it will include everything declared in all files in the mypackage
directory.
Importing a package
There are two general types of packages:
- The standard library (stdlib) packages
- All other packages
Standard library packages stand out because they don't list some repository information in their path, such as the following:
"fmt" "encoding/json" "archive/zip"
All other packages generally have repository information preceding them, as follows:
"github.com/johnsiilver/golib/lru" "github.com/kylelemons/godebug/pretty"
Note
A complete listing of stdlib
packages can be found at the following link: https://golang.org/pkg/.
To import packages, we use the import
keyword. So, let's import the standard library fmt
package and the mypackage
package, which lives at github.com/devopsforgo/mypackage
:
package main import ( "fmt" "github.com/devopsforgo/mypackage" )
It is important to note that the filenames are not part of the package path, but simply the directory path.
Using a package
Once you've imported a package, you can start accessing functions, types, or variables declared in the package by prefacing what you want to access with the name of the package and a period.
For example, the fmt
package has a function called Println()
that can be used to print a line to stdout. If we want to use it, it is as simple as the following:
fmt.Println("Hello!")
Package name conflicts
Let's say you have two packages named mypackage
. They both have the same name, so our program won't be able to tell which one we are referring to. You can rename a package import into whatever name you want:
import( "github.com/devopsforgo/mypackage" jpackage "github.com/johnsiilver/mypackage" )
jpackage
declares that in this package, we will refer to github.com/johnsiilver/mypackage
as jpackage
.
This ability allows us to use two similarly named packages as follows:
mypackage.Print() jpackage.Send()
Now, we will look at an important rule around packages that improves compile-time and binary size.
Packages must be used
Let's introduce you to the following rule: If you import a package, you must use it.
One of the things that the Go authors noticed about many of the other programming languages being used at Google was that they often had unused imports.
This was leading to compile times that were longer than needed and, in some cases, binary sizes that were much bigger than required. Python files were packaged in a proprietary format to ship around production, and some of these unused imports were adding hundreds of megabytes to the files.
To prevent these types of problems, Go will not compile a program that imports a package but doesn't use it, as shown here:
package main import ( "fmt" "sync" ) func main() { fmt.Println("Hello, playground") }
The preceding code outputs the following:
./prog.go:5:2: imported and not used: "sync"
In certain rare circumstances, you may need to do a side effects import, in which just loading the package causes something to happen, but you don't use the package. This should always be done in package main
and requires prepending with an underscore (_
):
package main import ( "fmt" _ "sync" //Just an example ) func main() { fmt.Println("Hello, playground") }
Next, we will declare a main package and discuss the basics of writing a Go program that imports a package.
A Go Hello World
Let's write a simple hello world program that is similar to the default program in the Go Playground. This example will demonstrate the following:
- Declaring a package
- Importing the
fmt
package from the standard library, which can print to our screen - Declaring the
main()
function of a program - Declaring a string variable using the
:=
operator - Printing the variable to the screen
Let's see what this looks like:
1 package main 2 3 import "fmt" 4 5 func main() { 6 hello := "Hello World!" 7 fmt.Println(hello) 8 9 }
In our first line, we declared the name of our package using the package
keyword. The entrance point for any Go binary is a package named main
that has a function called main()
.
In our third line, we import the fmt
package. fmt
has functions for doing string formatting and writing to various outputs.
On our fifth line, we declare a function called main
that takes no arguments and returns no values. main()
is special, as when a binary is run, it starts by running the main()
function.
Go uses {}
to show where a function starts and where a function ends (similar to C).
The sixth line declares a variable named hello
using the :=
operator. This operator indicates that we wish to create a new variable and assign it a value in a single line. This is the most common, but not the only, way to declare a variable.
As Go is typed, so :=
will assign the type based on the value. In this case, it will be a string, but if the value was an integer (such as 3
), it would be the in
t type, and if a floating-point (such as 2.4
), it would be the float64
type. If we wanted to declare a specific type, such as int8
or float32
, we would need some modifications (which we will talk about later).
On the seventh line, we call a function that is in the fmt
package called Println
. Println()
will print the contents of the hello
variable to stdout
followed by a new line character (\n
).
You will notice that the way to use a function declared in another package is to use the package name (without quotes) + a period + the name of the function. In this case, fmt.Println()
.
In this section, you have learned how to declare a package, import a package, what the function of the main
package is, and how to write a basic Go program with a variable declaration. In the next section, we will go into some depth on declaring and using variables.